From 725dae07e336ecfe5fe51970d6f31d4a8337b519 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 3 Sep 2017 10:43:16 -0700 Subject: Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..2d6605fe --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# dutil +dutil.lib - foundation library for all native code in WiX Toolset -- cgit v1.2.3-55-g6feb From 8e8da6dbc051ec884b5d439bb4f44dc027d05bbf Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 3 Sep 2017 10:47:32 -0700 Subject: Initialize repo --- .gitattributes | 2 + .gitignore | 295 ++++++++++++++++++++++++++++++++++++++++++++++ LICENSE.TXT | 28 +++++ appveyor.yml | 24 ++++ nuget.config | 7 ++ src/Cpp.Build.props | 9 ++ src/Directory.Build.props | 21 ++++ version.json | 11 ++ 8 files changed, 397 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE.TXT create mode 100644 appveyor.yml create mode 100644 nuget.config create mode 100644 src/Cpp.Build.props create mode 100644 src/Directory.Build.props create mode 100644 version.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3c6208a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,295 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs \ No newline at end of file diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 00000000..d4d316ef --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,28 @@ +Copyright (c) .NET Foundation and contributors. +This software is released under the Microsoft Reciprocal License (MS-RL) (the "License"); you may not use the software except in compliance with the License. + +The text of the Microsoft Reciprocal License (MS-RL) can be found online at: + http://opensource.org/licenses/ms-rl + + +Microsoft Reciprocal License (MS-RL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + A "contribution" is the original software, or any additions or changes to the software. + A "contributor" is any person that distributes its contribution under this license. + "Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose. + (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + (E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + (F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..fc70dbef --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,24 @@ +image: Visual Studio 2017 + +version: 0.0.0.{build} +configuration: Release +platform: + - x86 + - x64 + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..6e1ad9b5 --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props new file mode 100644 index 00000000..4fa2f590 --- /dev/null +++ b/src/Cpp.Build.props @@ -0,0 +1,9 @@ + + + + + + $(BaseIntermediateOutputPath) + $(OutputPath) + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..48ba462d --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,21 @@ + + + + + + Debug + $(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\ + $(MSBuildThisFileDirectory)..\build\$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + + + + $(MSBuildThisFileDirectory)..\..\ + + + + + diff --git a/version.json b/version.json new file mode 100644 index 00000000..5f857771 --- /dev/null +++ b/version.json @@ -0,0 +1,11 @@ +{ + "version": "4.0", + "publicReleaseRefSpec": [ + "^refs/heads/master$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} -- cgit v1.2.3-55-g6feb From 5d8375007754101ff2889d0e79486c8f9b7cf5ab Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 3 Sep 2017 11:22:38 -0700 Subject: Initial commit --- dutil.sln | 31 + src/dutil/acl2util.cpp | 121 + src/dutil/aclutil.cpp | 1030 ++++++++ src/dutil/apputil.cpp | 109 + src/dutil/apuputil.cpp | 658 +++++ src/dutil/atomutil.cpp | 1284 ++++++++++ src/dutil/buffutil.cpp | 419 ++++ src/dutil/butil.cpp | 271 ++ src/dutil/cabcutil.cpp | 1532 ++++++++++++ src/dutil/cabutil.cpp | 567 +++++ src/dutil/certutil.cpp | 327 +++ src/dutil/condutil.cpp | 20 + src/dutil/conutil.cpp | 656 +++++ src/dutil/cryputil.cpp | 379 +++ src/dutil/dictutil.cpp | 769 ++++++ src/dutil/dirutil.cpp | 395 +++ src/dutil/dlutil.cpp | 783 ++++++ src/dutil/dutil.cpp | 462 ++++ src/dutil/dutil.vcxproj | 300 +++ src/dutil/dutil.vcxproj.filters | 365 +++ src/dutil/eseutil.cpp | 1308 ++++++++++ src/dutil/fileutil.cpp | 1860 ++++++++++++++ src/dutil/gdiputil.cpp | 212 ++ src/dutil/guidutil.cpp | 39 + src/dutil/iis7util.cpp | 518 ++++ src/dutil/inc/aclutil.h | 154 ++ src/dutil/inc/apputil.h | 45 + src/dutil/inc/apuputil.h | 86 + src/dutil/inc/atomutil.h | 146 ++ src/dutil/inc/buffutil.h | 80 + src/dutil/inc/butil.h | 31 + src/dutil/inc/cabcutil.h | 62 + src/dutil/inc/cabutil.h | 56 + src/dutil/inc/certutil.h | 66 + src/dutil/inc/condutil.h | 19 + src/dutil/inc/conutil.h | 78 + src/dutil/inc/cryputil.h | 103 + src/dutil/inc/dictutil.h | 69 + src/dutil/inc/dirutil.h | 54 + src/dutil/inc/dlutil.h | 59 + src/dutil/inc/dutil.h | 160 ++ src/dutil/inc/eseutil.h | 223 ++ src/dutil/inc/fileutil.h | 235 ++ src/dutil/inc/gdiputil.h | 38 + src/dutil/inc/guidutil.h | 21 + src/dutil/inc/iis7util.h | 222 ++ src/dutil/inc/inetutil.h | 39 + src/dutil/inc/iniutil.h | 79 + src/dutil/inc/jsonutil.h | 112 + src/dutil/inc/locutil.h | 120 + src/dutil/inc/logutil.h | 190 ++ src/dutil/inc/memutil.h | 80 + src/dutil/inc/metautil.h | 52 + src/dutil/inc/monutil.h | 108 + src/dutil/inc/osutil.h | 39 + src/dutil/inc/pathutil.h | 232 ++ src/dutil/inc/perfutil.h | 24 + src/dutil/inc/polcutil.h | 39 + src/dutil/inc/procutil.h | 75 + src/dutil/inc/regutil.h | 233 ++ src/dutil/inc/resrutil.h | 43 + src/dutil/inc/reswutil.h | 31 + src/dutil/inc/rexutil.h | 54 + src/dutil/inc/rmutil.h | 46 + src/dutil/inc/rssutil.h | 89 + src/dutil/inc/sceutil.h | 273 ++ src/dutil/inc/sczutil.h | 30 + src/dutil/inc/shelutil.h | 47 + src/dutil/inc/sqlutil.h | 136 + src/dutil/inc/srputil.h | 45 + src/dutil/inc/strutil.h | 311 +++ src/dutil/inc/svcutil.h | 21 + src/dutil/inc/thmutil.h | 711 ++++++ src/dutil/inc/timeutil.h | 38 + src/dutil/inc/uncutil.h | 20 + src/dutil/inc/uriutil.h | 100 + src/dutil/inc/userutil.h | 32 + src/dutil/inc/varutil.h | 126 + src/dutil/inc/wiutil.h | 373 +++ src/dutil/inc/wuautil.h | 19 + src/dutil/inc/xmlutil.h | 167 ++ src/dutil/inetutil.cpp | 137 + src/dutil/iniutil.cpp | 753 ++++++ src/dutil/jsonutil.cpp | 672 +++++ src/dutil/locutil.cpp | 613 +++++ src/dutil/logutil.cpp | 942 +++++++ src/dutil/memutil.cpp | 323 +++ src/dutil/metautil.cpp | 356 +++ src/dutil/monutil.cpp | 2004 +++++++++++++++ src/dutil/osutil.cpp | 188 ++ src/dutil/path2utl.cpp | 89 + src/dutil/pathutil.cpp | 1009 ++++++++ src/dutil/perfutil.cpp | 67 + src/dutil/polcutil.cpp | 111 + src/dutil/precomp.h | 95 + src/dutil/proc2utl.cpp | 68 + src/dutil/proc3utl.cpp | 114 + src/dutil/procutil.cpp | 488 ++++ src/dutil/regutil.cpp | 926 +++++++ src/dutil/resrutil.cpp | 251 ++ src/dutil/reswutil.cpp | 368 +++ src/dutil/rexutil.cpp | 586 +++++ src/dutil/rmutil.cpp | 473 ++++ src/dutil/rssutil.cpp | 632 +++++ src/dutil/sceutil.cpp | 2489 +++++++++++++++++++ src/dutil/shelutil.cpp | 327 +++ src/dutil/sqlutil.cpp | 868 +++++++ src/dutil/srputil.cpp | 237 ++ src/dutil/strutil.cpp | 2730 ++++++++++++++++++++ src/dutil/svcutil.cpp | 44 + src/dutil/thmutil.cpp | 5226 +++++++++++++++++++++++++++++++++++++++ src/dutil/timeutil.cpp | 370 +++ src/dutil/uncutil.cpp | 54 + src/dutil/uriutil.cpp | 538 ++++ src/dutil/userutil.cpp | 285 +++ src/dutil/varutil.cpp | 274 ++ src/dutil/wiutil.cpp | 1494 +++++++++++ src/dutil/wuautil.cpp | 89 + src/dutil/xmlutil.cpp | 1317 ++++++++++ src/dutil/xsd/thmutil.xsd | 1188 +++++++++ 120 files changed, 49351 insertions(+) create mode 100644 dutil.sln create mode 100644 src/dutil/acl2util.cpp create mode 100644 src/dutil/aclutil.cpp create mode 100644 src/dutil/apputil.cpp create mode 100644 src/dutil/apuputil.cpp create mode 100644 src/dutil/atomutil.cpp create mode 100644 src/dutil/buffutil.cpp create mode 100644 src/dutil/butil.cpp create mode 100644 src/dutil/cabcutil.cpp create mode 100644 src/dutil/cabutil.cpp create mode 100644 src/dutil/certutil.cpp create mode 100644 src/dutil/condutil.cpp create mode 100644 src/dutil/conutil.cpp create mode 100644 src/dutil/cryputil.cpp create mode 100644 src/dutil/dictutil.cpp create mode 100644 src/dutil/dirutil.cpp create mode 100644 src/dutil/dlutil.cpp create mode 100644 src/dutil/dutil.cpp create mode 100644 src/dutil/dutil.vcxproj create mode 100644 src/dutil/dutil.vcxproj.filters create mode 100644 src/dutil/eseutil.cpp create mode 100644 src/dutil/fileutil.cpp create mode 100644 src/dutil/gdiputil.cpp create mode 100644 src/dutil/guidutil.cpp create mode 100644 src/dutil/iis7util.cpp create mode 100644 src/dutil/inc/aclutil.h create mode 100644 src/dutil/inc/apputil.h create mode 100644 src/dutil/inc/apuputil.h create mode 100644 src/dutil/inc/atomutil.h create mode 100644 src/dutil/inc/buffutil.h create mode 100644 src/dutil/inc/butil.h create mode 100644 src/dutil/inc/cabcutil.h create mode 100644 src/dutil/inc/cabutil.h create mode 100644 src/dutil/inc/certutil.h create mode 100644 src/dutil/inc/condutil.h create mode 100644 src/dutil/inc/conutil.h create mode 100644 src/dutil/inc/cryputil.h create mode 100644 src/dutil/inc/dictutil.h create mode 100644 src/dutil/inc/dirutil.h create mode 100644 src/dutil/inc/dlutil.h create mode 100644 src/dutil/inc/dutil.h create mode 100644 src/dutil/inc/eseutil.h create mode 100644 src/dutil/inc/fileutil.h create mode 100644 src/dutil/inc/gdiputil.h create mode 100644 src/dutil/inc/guidutil.h create mode 100644 src/dutil/inc/iis7util.h create mode 100644 src/dutil/inc/inetutil.h create mode 100644 src/dutil/inc/iniutil.h create mode 100644 src/dutil/inc/jsonutil.h create mode 100644 src/dutil/inc/locutil.h create mode 100644 src/dutil/inc/logutil.h create mode 100644 src/dutil/inc/memutil.h create mode 100644 src/dutil/inc/metautil.h create mode 100644 src/dutil/inc/monutil.h create mode 100644 src/dutil/inc/osutil.h create mode 100644 src/dutil/inc/pathutil.h create mode 100644 src/dutil/inc/perfutil.h create mode 100644 src/dutil/inc/polcutil.h create mode 100644 src/dutil/inc/procutil.h create mode 100644 src/dutil/inc/regutil.h create mode 100644 src/dutil/inc/resrutil.h create mode 100644 src/dutil/inc/reswutil.h create mode 100644 src/dutil/inc/rexutil.h create mode 100644 src/dutil/inc/rmutil.h create mode 100644 src/dutil/inc/rssutil.h create mode 100644 src/dutil/inc/sceutil.h create mode 100644 src/dutil/inc/sczutil.h create mode 100644 src/dutil/inc/shelutil.h create mode 100644 src/dutil/inc/sqlutil.h create mode 100644 src/dutil/inc/srputil.h create mode 100644 src/dutil/inc/strutil.h create mode 100644 src/dutil/inc/svcutil.h create mode 100644 src/dutil/inc/thmutil.h create mode 100644 src/dutil/inc/timeutil.h create mode 100644 src/dutil/inc/uncutil.h create mode 100644 src/dutil/inc/uriutil.h create mode 100644 src/dutil/inc/userutil.h create mode 100644 src/dutil/inc/varutil.h create mode 100644 src/dutil/inc/wiutil.h create mode 100644 src/dutil/inc/wuautil.h create mode 100644 src/dutil/inc/xmlutil.h create mode 100644 src/dutil/inetutil.cpp create mode 100644 src/dutil/iniutil.cpp create mode 100644 src/dutil/jsonutil.cpp create mode 100644 src/dutil/locutil.cpp create mode 100644 src/dutil/logutil.cpp create mode 100644 src/dutil/memutil.cpp create mode 100644 src/dutil/metautil.cpp create mode 100644 src/dutil/monutil.cpp create mode 100644 src/dutil/osutil.cpp create mode 100644 src/dutil/path2utl.cpp create mode 100644 src/dutil/pathutil.cpp create mode 100644 src/dutil/perfutil.cpp create mode 100644 src/dutil/polcutil.cpp create mode 100644 src/dutil/precomp.h create mode 100644 src/dutil/proc2utl.cpp create mode 100644 src/dutil/proc3utl.cpp create mode 100644 src/dutil/procutil.cpp create mode 100644 src/dutil/regutil.cpp create mode 100644 src/dutil/resrutil.cpp create mode 100644 src/dutil/reswutil.cpp create mode 100644 src/dutil/rexutil.cpp create mode 100644 src/dutil/rmutil.cpp create mode 100644 src/dutil/rssutil.cpp create mode 100644 src/dutil/sceutil.cpp create mode 100644 src/dutil/shelutil.cpp create mode 100644 src/dutil/sqlutil.cpp create mode 100644 src/dutil/srputil.cpp create mode 100644 src/dutil/strutil.cpp create mode 100644 src/dutil/svcutil.cpp create mode 100644 src/dutil/thmutil.cpp create mode 100644 src/dutil/timeutil.cpp create mode 100644 src/dutil/uncutil.cpp create mode 100644 src/dutil/uriutil.cpp create mode 100644 src/dutil/userutil.cpp create mode 100644 src/dutil/varutil.cpp create mode 100644 src/dutil/wiutil.cpp create mode 100644 src/dutil/wuautil.cpp create mode 100644 src/dutil/xmlutil.cpp create mode 100644 src/dutil/xsd/thmutil.xsd diff --git a/dutil.sln b/dutil.sln new file mode 100644 index 00000000..5f83f097 --- /dev/null +++ b/dutil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.12 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133} + EndGlobalSection +EndGlobal diff --git a/src/dutil/acl2util.cpp b/src/dutil/acl2util.cpp new file mode 100644 index 00000000..2261abe3 --- /dev/null +++ b/src/dutil/acl2util.cpp @@ -0,0 +1,121 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** +AclCalculateServiceSidString - gets the SID string for the given service name + +NOTE: psczSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclCalculateServiceSidString( + __in LPCWSTR wzServiceName, + __in int cchServiceName, + __deref_out_z LPWSTR* psczSid + ) +{ + // TODO: use undocumented RtlCreateServiceSid function? + // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx + // Assume little endian. + HRESULT hr = S_OK; + LPWSTR sczUpperServiceName = NULL; + DWORD cbHash = SHA1_HASH_LEN; + BYTE* pbHash = NULL; + + Assert(psczSid); + + if (0 == cchServiceName) + { + hr = ::StringCchLengthW(wzServiceName, INT_MAX, reinterpret_cast(&cchServiceName)); + ExitOnFailure(hr, "Failed to get the length of the service name."); + } + + hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName); + ExitOnFailure(hr, "Failed to upper case the service name."); + + pbHash = reinterpret_cast(MemAlloc(cbHash, TRUE)); + ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); + + hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * 2, PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); + ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); + + hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u", + MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])), + MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])), + MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])), + MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])), + MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19]))); + +LExit: + ReleaseMem(pbHash); + ReleaseStr(sczUpperServiceName); + + return hr; +} + + +/******************************************************************** +AclGetAccountSidStringEx - gets a string version of the account's SID + calculates a service's SID if lookup fails + +NOTE: psczSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSidStringEx( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* psczSid + ) +{ + HRESULT hr = S_OK; + int cchAccount = 0; + PSID psid = NULL; + LPWSTR pwz = NULL; + LPWSTR sczSid = NULL; + + Assert(psczSid); + + hr = AclGetAccountSid(wzSystem, wzAccount, &psid); + if (SUCCEEDED(hr)) + { + Assert(::IsValidSid(psid)); + + if (!::ConvertSidToStringSidW(psid, &pwz)) + { + ExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount); + } + + hr = StrAllocString(psczSid, pwz, 0); + } + else + { + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) + { + HRESULT hrLength = ::StringCchLengthW(wzAccount, INT_MAX, reinterpret_cast(&cchAccount)); + ExitOnFailure(hrLength, "Failed to get the length of the account name."); + + if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11)) + { + // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it. + LPCWSTR wzServiceName = &wzAccount[11]; + hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid); + ExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName); + + *psczSid = sczSid; + sczSid = NULL; + } + } + ExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount); + } + +LExit: + ReleaseStr(sczSid); + if (pwz) + { + ::LocalFree(pwz); + } + if (psid) + { + AclFreeSid(psid); + } + + return hr; +} diff --git a/src/dutil/aclutil.cpp b/src/dutil/aclutil.cpp new file mode 100644 index 00000000..fc01ecc8 --- /dev/null +++ b/src/dutil/aclutil.cpp @@ -0,0 +1,1030 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** +AclCheckAccess - determines if token has appropriate privileges + +NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero +if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckAccess( + __in HANDLE hToken, + __in ACL_ACCESS* paa + ) +{ + HRESULT hr = S_OK; + PSID psid = NULL; + BOOL fIsMember = FALSE; + + ExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check"); + Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask); + + if (paa->pwzAccountName) + { + hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid); + ExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName); + } + else + { + 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)) + { + ExitWithLastError(hr, "failed to initialize SID"); + } + } + + if (!::CheckTokenMembership(hToken, psid, &fIsMember)) + { + ExitWithLastError(hr, "failed to check membership"); + } + + fIsMember ? hr = S_OK : hr = S_FALSE; + +LExit: + if (psid) + { + ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid? + } + + return hr; +} + + +/******************************************************************** +AclCheckAdministratorAccess - determines if token has Administrator privileges + +NOTE: if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckAdministratorAccess( + __in HANDLE hToken + ) +{ + ACL_ACCESS aa; + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; + + memset(&aa, 0, sizeof(aa)); + aa.sia = siaNt; + aa.nSubAuthorityCount = 2; + aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID; + aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS; + + return AclCheckAccess(hToken, &aa); +} + + +/******************************************************************** +AclCheckLocalSystemAccess - determines if token has LocalSystem privileges + +NOTE: if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckLocalSystemAccess( + __in HANDLE hToken + ) +{ + ACL_ACCESS aa; + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; + + memset(&aa, 0, sizeof(aa)); + aa.sia = siaNt; + aa.nSubAuthorityCount = 1; + aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID; + + return AclCheckAccess(hToken, &aa); +} + + +/******************************************************************** +AclGetWellKnownSid - returns a SID for the specified account + +********************************************************************/ +extern "C" HRESULT DAPI AclGetWellKnownSid( + __in WELL_KNOWN_SID_TYPE wkst, + __deref_out PSID* ppsid + ) +{ + Assert(ppsid); + + HRESULT hr = S_OK;; + PSID psid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + + PSID psidTemp = NULL; +#if(_WIN32_WINNT < 0x0501) + SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; + SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY; + BOOL fSuccess = FALSE; +#endif + + // + // allocate memory for the SID and get it + // + psid = static_cast(MemAlloc(cbSid, TRUE)); + ExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID"); + +#if(_WIN32_WINNT < 0x0501) + switch (wkst) + { + case WinWorldSid: // Everyone + fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinAuthenticatedUserSid: // Authenticated Users + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinLocalSystemSid: // LocalSystem + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinLocalServiceSid: // LocalService + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinNetworkServiceSid: // NetworkService + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinGuestsSid: // Guests + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinAdministratorsSid: // Administrators + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinUsersSid: // Users + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinCreatorOwnerSid: //CREATOR OWNER + fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinInteractiveSid: // INTERACTIVE + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "unknown well known SID: %d", wkst); + } + + if (!fSuccess) + ExitOnLastError(hr, "failed to allocate well known SID: %d", wkst); + + if (!::CopySid(cbSid, psid, psidTemp)) + ExitOnLastError(hr, "failed to create well known SID: %d", wkst); +#else + Assert(NULL == psidTemp); + if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid)) + { + ExitWithLastError(hr, "failed to create well known SID: %d", wkst); + } +#endif + + *ppsid = psid; + psid = NULL; // null it here so it won't be released below + + Assert(S_OK == hr && ::IsValidSid(*ppsid)); +LExit: + if (psidTemp) + { + ::FreeSid(psidTemp); + } + + ReleaseMem(psid); + + return hr; +} + + +/******************************************************************** +AclGetAccountSid - returns a SID for the specified account + +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSid( + __in_opt LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out PSID* ppsid + ) +{ + Assert(wzAccount && *wzAccount && ppsid); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + PSID psid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + LPWSTR pwzDomainName = NULL; + DWORD cbDomainName = 255; + SID_NAME_USE peUse; + + // + // allocate memory for the SID and domain name + // + psid = static_cast(MemAlloc(cbSid, TRUE)); + ExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID"); + hr = StrAlloc(&pwzDomainName, cbDomainName); + ExitOnFailure(hr, "failed to allocate string for domain name"); + + // + // try to lookup the account now + // + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) + { + // if one of the buffers wasn't large enough + er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + if (SECURITY_MAX_SID_SIZE < cbSid) + { + PSID psidNew = static_cast(MemReAlloc(psid, cbSid, TRUE)); + ExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount); + + psid = psidNew; + } + if (255 < cbDomainName) + { + hr = StrAlloc(&pwzDomainName, cbDomainName); + ExitOnFailure(hr, "failed to allocate string for domain name"); + } + + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) + { + ExitWithLastError(hr, "failed to lookup account: %ls", wzAccount); + } + } + else + { + ExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount); + } + } + + *ppsid = psid; + psid = NULL; + + hr = S_OK; +LExit: + ReleaseStr(pwzDomainName); + ReleaseMem(psid); + + return hr; +} + + +/******************************************************************** +AclGetAccountSidString - gets a string version of the user's SID + +NOTE: ppwzSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSidString( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* ppwzSid + ) +{ + Assert(ppwzSid); + HRESULT hr = S_OK; + PSID psid = NULL; + LPWSTR pwz = NULL; + + *ppwzSid = NULL; + + hr = AclGetAccountSid(wzSystem, wzAccount, &psid); + ExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount); + Assert(::IsValidSid(psid)); + + if (!::ConvertSidToStringSidW(psid, &pwz)) + { + ExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount); + } + + hr = StrAllocString(ppwzSid, pwz, 0); + +LExit: + if (FAILED(hr)) + { + ReleaseNullStr(*ppwzSid); + } + + if (pwz) + { + ::LocalFree(pwz); + } + + if (psid) + { + AclFreeSid(psid); + } + + return hr; +} + + +/******************************************************************** +AclCreateDacl - creates a DACL from ACL_ACE structures + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateDacl( + __in_ecount(cDeny) ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount(cAllow) ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAcl + ) +{ + Assert(ppAcl); + HRESULT hr = S_OK; + ACL* pAcl = NULL; + DWORD cbAcl = 0; + DWORD i; + + *ppAcl = NULL; + + // initialize the ACL + cbAcl = sizeof(ACL); + for (i = 0; i < cDeny; ++i) + { + cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD); + } + + for (i = 0; i < cAllow; ++i) + { + cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD); + } + + pAcl = static_cast(MemAlloc(cbAcl, TRUE)); + ExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION)) +#pragma prefast(pop) + { + ExitWithLastError(hr, "failed to initialize ACL"); + } + + // add in the ACEs (denied first) + for (i = 0; i < cDeny; ++i) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid)) +#pragma prefast(pop) + { + ExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i); + } + } + for (i = 0; i < cAllow; ++i) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid)) +#pragma prefast(pop) + { + ExitWithLastError(hr, "failed to add access allowed ACE #$d to ACL", i); + } + } + + *ppAcl = pAcl; + pAcl = NULL; + AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL"); + Assert(S_OK == hr); +LExit: + if (pAcl) + { + AclFreeDacl(pAcl); + } + + return hr; +} + + +/******************************************************************** +AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure + +********************************************************************/ +extern "C" HRESULT DAPI AclAddToDacl( + __in ACL* pAcl, + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAclNew + ) +{ + Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew); + HRESULT hr = S_OK; + + ACL_SIZE_INFORMATION asi; + ACL_ACE* paaNewDeny = NULL; + DWORD cNewDeny = 0; + ACL_ACE* paaNewAllow = NULL; + DWORD cNewAllow = 0; + + ACCESS_ALLOWED_ACE* paaa; + ACCESS_DENIED_ACE* pada; + DWORD i; + + // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay) + if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation)) + { + ExitWithLastError(hr, "failed to get information about original ACL"); + } + + if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow + (asi.AceCount + cDeny) < cDeny || // check for overflow + (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE)) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny)); + } + + paaNewDeny = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE)); + ExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs"); + + if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow + (asi.AceCount + cAllow) < cAllow || // check for overflow + (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE)) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow)); + } + + paaNewAllow = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE)); + ExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs"); + + // fill in the new structures with old data then new data (denied first) + for (i = 0; i < asi.AceCount; ++i) + { + if (!::GetAce(pAcl, i, reinterpret_cast(&pada))) + { + ExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + } + + if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType) + { + continue; // skip non-denied aces + } + + paaNewDeny[i].dwFlags = pada->Header.AceFlags; + paaNewDeny[i].dwMask = pada->Mask; + paaNewDeny[i].psid = reinterpret_cast(&(pada->SidStart)); + ++cNewDeny; + } + + memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny); + cNewDeny += cDeny; + + + for (i = 0; i < asi.AceCount; ++i) + { + if (!::GetAce(pAcl, i, reinterpret_cast(&paaa))) + { + ExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + } + + if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType) + { + continue; // skip non-allowed aces + } + + paaNewAllow[i].dwFlags = paaa->Header.AceFlags; + paaNewAllow[i].dwMask = paaa->Mask; + paaNewAllow[i].psid = reinterpret_cast(&(paaa->SidStart)); + ++cNewAllow; + } + + memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow); + cNewAllow += cAllow; + + // create the dacl with the new + hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew); + ExitOnFailure(hr, "failed to create new ACL from existing ACL"); + + AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL"); + Assert(S_OK == hr); +LExit: + ReleaseMem(paaNewAllow); + ReleaseMem(paaNewDeny); + + return hr; +} + + +/******************************************************************** +AclMergeDacls - creates a new DACL from two existing ACLs + +********************************************************************/ +extern "C" HRESULT DAPI AclMergeDacls( + __in const ACL* pAcl1, + __in const ACL* pAcl2, + __deref_out ACL** ppAclNew + ) +{ + HRESULT hr = E_NOTIMPL; + + Assert(pAcl1 && pAcl2 && ppAclNew); + UNREFERENCED_PARAMETER(pAcl1); + UNREFERENCED_PARAMETER(pAcl2); + UNREFERENCED_PARAMETER(ppAclNew); + +//LExit: + return hr; +} + + +/******************************************************************** +AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateDaclOld( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out ACL** ppACL + ) +{ + Assert(ppACL); + HRESULT hr = S_OK; + DWORD* pdwAccessMask = NULL; + PSID* ppsid = NULL; + + DWORD i; + int cbAcl; + + *ppACL = NULL; + + // + // create the SIDs and calculate the space for the ACL + // + pdwAccessMask = static_cast(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE)); + ExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask"); + ppsid = static_cast(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE)); + ExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid"); + + cbAcl = sizeof (ACL); // start with the size of the header + for (i = 0; i < cAclAccesses; ++i) + { + if (paa[i].pwzAccountName) + { + hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i); + ExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName); + } + else + { + if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount, + paa[i].nSubAuthority[0], paa[i].nSubAuthority[1], + paa[i].nSubAuthority[2], paa[i].nSubAuthority[3], + paa[i].nSubAuthority[4], paa[i].nSubAuthority[5], + paa[i].nSubAuthority[6], paa[i].nSubAuthority[7], + (void**)(ppsid + i)))) + { + ExitWithLastError(hr, "failed to initialize SIDs #%u", i); + } + } + + // add the newly allocated SID size to the count of bytes for this ACL + cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD); + if (paa[i].fDenyAccess) + { + cbAcl += sizeof(ACCESS_DENIED_ACE); + } + else + { + cbAcl += sizeof(ACCESS_ALLOWED_ACE); + } + + pdwAccessMask[i] = paa[i].dwAccessMask; + } + + // + // allocate the ACL and set the appropriate ACEs + // + *ppACL = static_cast(MemAlloc(cbAcl, FALSE)); + ExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION)) +#pragma prefast(pop) + { + ExitWithLastError(hr, "failed to initialize ACLs"); + } + + // add an access-allowed ACE for each of the SIDs + for (i = 0; i < cAclAccesses; ++i) + { + if (paa[i].fDenyAccess) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) +#pragma prefast(pop) + { + ExitWithLastError(hr, "failed to add access denied for ACE"); + } + } + else + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) +#pragma prefast(pop) + { + ExitWithLastError(hr, "failed to add access allowed for ACE"); + } + } + } + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(*ppACL); + } + + if (ppsid) + { + for (i = 0; i < cAclAccesses; ++i) + { + if (ppsid[i]) + { + ::FreeSid(ppsid[i]); + } + } + + MemFree(ppsid); + } + + ReleaseMem(pdwAccessMask); + + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptorFromDacl - creates a self-relative security +descriptor from an existing DACL + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( + __in ACL* pACL, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + + SECURITY_DESCRIPTOR sd; + DWORD cbSD; + + ExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided"); + ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided"); + + *ppsd = NULL; + + // + // create the absolute security descriptor + // + + // initialize our security descriptor, throw the ACL into it, and set the owner +#pragma prefast(push) +#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs +#pragma prefast(disable:25029) + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) || + (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) || + (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE))) +#pragma prefast(pop) + { + ExitWithLastError(hr, "failed to initialize security descriptor"); + } + + // + // create the self-relative security descriptor + // + cbSD = ::GetSecurityDescriptorLength(&sd); + *ppsd = static_cast(MemAlloc(cbSD, FALSE)); + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptor - creates a self-relative security descriptor from an +ACL_ACCESS structure + +NOTE: ppsd should be freed with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptor( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + Assert(ppsd); + HRESULT hr = S_OK; + + ACL* pACL; + + *ppsd = NULL; + + // + // create the DACL + // + hr = AclCreateDaclOld(paa, cAclAccesses, &pACL); + ExitOnFailure(hr, "failed to create DACL for security descriptor"); + + // + // create self-relative security descriptor + // + hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd); + +LExit: + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptorFromString - creates a self-relative security +descriptor from an SDDL string + +NOTE: ppsd should be freed with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString( + __deref_out SECURITY_DESCRIPTOR** ppsd, + __in_z __format_string LPCWSTR wzSddlFormat, + ... + ) +{ + Assert(ppsd); + HRESULT hr = S_OK; + LPWSTR pwzSddl = NULL; + va_list args; + PSECURITY_DESCRIPTOR psd = NULL; + DWORD cbSD = 0; + + *ppsd = NULL; + + va_start(args, wzSddlFormat); + hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args); + va_end(args); + ExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat); + + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD)) + { + ExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl); + } + + *ppsd = static_cast(MemAlloc(cbSD, FALSE)); + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + + Assert(S_OK == hr); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + if (psd) + { + ::LocalFree(psd); + } + + ReleaseStr(pwzSddl); + return hr; +} + + +/******************************************************************** +AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor + +NOTE: passed in security descriptor must be in self-relative format +********************************************************************/ +extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + DWORD cbSD; + + ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided"); + *ppsd = NULL; + + // + // create the self-relative security descriptor + // + cbSD = ::GetSecurityDescriptorLength(psd); + *ppsd = static_cast(MemAlloc(cbSD, 0)); + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + return hr; +} + + +/******************************************************************** +AclGetSecurityDescriptor - returns self-relative security descriptor for named object + +NOTE: free ppsd with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclGetSecurityDescriptor( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + DWORD er; + PSECURITY_DESCRIPTOR psd = NULL; + DWORD cbSD; + + ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided"); + *ppsd = NULL; + + // get the security descriptor for the object + er = ::GetNamedSecurityInfoW(const_cast(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd); + ExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject); + Assert(::IsValidSecurityDescriptor(psd)); + + // copy the self-relative security descriptor + cbSD = ::GetSecurityDescriptorLength(psd); + *ppsd = static_cast(MemAlloc(cbSD, 0)); + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + if (psd) + { + ::LocalFree(psd); + } + + return hr; +} + + +extern "C" HRESULT DAPI AclSetSecurityWithRetry( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __in_opt PSID psidOwner, + __in_opt PSID psidGroup, + __in_opt PACL pDacl, + __in_opt PACL pSacl, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + HRESULT hr = S_OK; + LPWSTR sczObject = NULL; + DWORD i = 0; + + hr = StrAllocString(&sczObject, wzObject, 0); + ExitOnFailure(hr, "Failed to copy object to secure."); + + hr = E_FAIL; + for (i = 0; FAILED(hr) && i <= cRetry; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl); + hr = HRESULT_FROM_WIN32(er); + } + ExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i); + +LExit: + ReleaseStr(sczObject); + + return hr; +} + + +/******************************************************************** +AclFreeSid - frees a SID created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeSid( + __in PSID psid + ) +{ + Assert(psid && ::IsValidSid(psid)); + HRESULT hr = S_OK; + + hr = MemFree(psid); + + return hr; +} + + +/******************************************************************** +AclFreeDacl - frees a DACL created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeDacl( + __in ACL* pACL + ) +{ + Assert(pACL); + HRESULT hr = S_OK; + + hr = MemFree(pACL); + + return hr; +} + + +/******************************************************************** +AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd + ) +{ + Assert(psd && ::IsValidSecurityDescriptor(psd)); + HRESULT hr = S_OK; + + hr = MemFree(psd); + + return hr; +} + + +/******************************************************************** +AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor + +********************************************************************/ +extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor( + __in SECURITY_DESCRIPTOR* pSecurity, + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew + ) +{ + HRESULT hr = S_OK; + PACL pAcl = NULL; + PACL pAclNew = NULL; + BOOL fValid, fDaclDefaulted; + ACL_ACE ace[1]; + SECURITY_DESCRIPTOR* pSecurityNew; + + if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid) + { + ExitOnLastError(hr, "Failed to get acl from security descriptor"); + } + + hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid); + ExitOnFailure(hr, "failed to get sid for Administrators group"); + + ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE; + ace[0].dwMask = GENERIC_ALL; + + hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew); + ExitOnFailure(hr, "failed to add Administrators ACE to ACL"); + + hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew); + ExitOnLastError(hr, "Failed to create new security descriptor"); + + // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it. + pAclNew = NULL; + + *ppSecurityNew = pSecurityNew; + +LExit: + if (pAclNew) + { + AclFreeDacl(pAclNew); + } + if (ace[0].psid) + { + AclFreeSid(ace[0].psid); + } + + return hr; +} diff --git a/src/dutil/apputil.cpp b/src/dutil/apputil.cpp new file mode 100644 index 00000000..8562a47a --- /dev/null +++ b/src/dutil/apputil.cpp @@ -0,0 +1,109 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; +typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); +typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); + +extern "C" void DAPI AppFreeCommandLineArgs( + __in LPWSTR* argv + ) +{ + // The "ignored" hack in AppParseCommandLine requires an adjustment. + LPWSTR* argvOriginal = argv - 1; + ::LocalFree(argvOriginal); +} + +/******************************************************************** +AppInitialize - initializes the standard safety precautions for an + installation application. + +********************************************************************/ +extern "C" void DAPI AppInitialize( + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], + __in DWORD cSafelyLoadSystemDlls + ) +{ + HRESULT hr = S_OK; + HMODULE hIgnored = NULL; + BOOL fSetDefaultDllDirectories = FALSE; + + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + + // Best effort call to initialize default DLL directories to system only. + HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32"); + LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories"); + if (pfnSetDefaultDllDirectories) + { + if (pfnSetDefaultDllDirectories(PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32)) + { + fSetDefaultDllDirectories = TRUE; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to call SetDefaultDllDirectories."); + } + } + + // Only need to safely load if the default DLL directories was not + // able to be set. + if (!fSetDefaultDllDirectories) + { + // Remove current working directory from search order. + LPFN_SETDLLDIRECTORYW pfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW"); + if (!pfnSetDllDirectory || !pfnSetDllDirectory(L"")) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to call SetDllDirectory."); + } + + for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i) + { + hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored); + if (FAILED(hr)) + { + TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]); + } + } + } +} + +extern "C" void DAPI AppInitializeUnsafe() +{ + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); +} + +extern "C" DAPI_(HRESULT) AppParseCommandLine( + __in LPCWSTR wzCommandLine, + __in int* pArgc, + __in LPWSTR** pArgv + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCommandLine = NULL; + LPWSTR* argv = NULL; + int argc = 0; + + // CommandLineToArgvW tries to treat the first argument as the path to the process, + // which fails pretty miserably if your first argument is something like + // FOO="C:\Program Files\My Company". So give it something harmless to play with. + hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0); + ExitOnFailure(hr, "Failed to initialize command line."); + + hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0); + ExitOnFailure(hr, "Failed to copy command line."); + + argv = ::CommandLineToArgvW(sczCommandLine, &argc); + ExitOnNullWithLastError(argv, hr, "Failed to parse command line."); + + // Skip "ignored" argument/hack. + *pArgv = argv + 1; + *pArgc = argc - 1; + +LExit: + ReleaseStr(sczCommandLine); + + return hr; +} diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp new file mode 100644 index 00000000..11aaf3f2 --- /dev/null +++ b/src/dutil/apuputil.cpp @@ -0,0 +1,658 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define SHA256_DIGEST_LEN 32 + +// prototypes +static HRESULT ProcessEntry( + __in ATOM_ENTRY* pAtomEntry, + __in LPCWSTR wzDefaultAppId, + __out APPLICATION_UPDATE_ENTRY* pApupEntry + ); +static HRESULT ParseEnclosure( + __in ATOM_LINK* pLink, + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ); +static __callback int __cdecl CompareEntries( + void* pvContext, + const void* pvLeft, + const void* pvRight + ); +static HRESULT FilterEntries( + __in APPLICATION_UPDATE_ENTRY* rgEntries, + __in DWORD cEntries, + __in DWORD64 dw64CurrentVersion, + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, + __inout DWORD* pcFilteredEntries + ); +static HRESULT CopyEntry( + __in const APPLICATION_UPDATE_ENTRY* pSrc, + __in APPLICATION_UPDATE_ENTRY* pDest + ); +static HRESULT CopyEnclosure( + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, + __in APPLICATION_UPDATE_ENCLOSURE* pDest + ); +static void FreeEntry( + __in APPLICATION_UPDATE_ENTRY* pApupEntry + ); +static void FreeEnclosure( + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ); + + +// +// ApupCalculateChainFromAtom - returns the chain of application updates found in an ATOM feed. +// +extern "C" HRESULT DAPI ApupAllocChainFromAtom( + __in ATOM_FEED* pFeed, + __out APPLICATION_UPDATE_CHAIN** ppChain + ) +{ + HRESULT hr = S_OK; + APPLICATION_UPDATE_CHAIN* pChain = NULL; + + pChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); + + // First search the ATOM feed's custom elements to try and find the default application identity. + for (ATOM_UNKNOWN_ELEMENT* pElement = pFeed->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) + { + hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0); + ExitOnFailure(hr, "Failed to allocate default application id."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) + { + hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0); + ExitOnFailure(hr, "Failed to allocate default application type."); + } + } + } + } + } + + // Assume there will be as many application updates entries as their are feed entries. + if (pFeed->cEntries) + { + pChain->rgEntries = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE)); + ExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries."); + + // Process each entry, building up the chain. + for (DWORD i = 0; i < pFeed->cEntries; ++i) + { + hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries); + ExitOnFailure(hr, "Failed to process ATOM entry."); + + if (S_FALSE != hr) + { + ++pChain->cEntries; + } + } + + // Sort the chain by descending version and ascending total size. + qsort_s(pChain->rgEntries, pChain->cEntries, sizeof(APPLICATION_UPDATE_ENTRY), CompareEntries, NULL); + } + + // Trim the unused entries from the end, if any of the entries failed to parse or validate + if (pChain->cEntries != pFeed->cEntries) + { + if (pChain->cEntries > 0) + { + pChain->rgEntries = static_cast(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE)); + ExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries."); + } + else + { + ReleaseNullMem(pChain->rgEntries); + } + } + + *ppChain = pChain; + pChain = NULL; + +LExit: + ReleaseApupChain(pChain); + + return hr; +} + + +// +// ApupFilterChain - remove the unneeded update elements from the chain. +// +HRESULT DAPI ApupFilterChain( + __in APPLICATION_UPDATE_CHAIN* pChain, + __in DWORD64 dw64Version, + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain + ) +{ + HRESULT hr = S_OK; + APPLICATION_UPDATE_CHAIN* pNewChain = NULL; + APPLICATION_UPDATE_ENTRY* prgEntries = NULL; + DWORD cEntries = NULL; + + pNewChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); + ExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); + + hr = FilterEntries(pChain->rgEntries, pChain->cEntries, dw64Version, &prgEntries, &cEntries); + ExitOnFailure(hr, "Failed to filter entries by version."); + + if (pChain->wzDefaultApplicationId) + { + hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0); + ExitOnFailure(hr, "Failed to copy default application id."); + } + + if (pChain->wzDefaultApplicationType) + { + hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0); + ExitOnFailure(hr, "Failed to copy default application type."); + } + + pNewChain->rgEntries = prgEntries; + pNewChain->cEntries = cEntries; + + *ppFilteredChain = pNewChain; + pNewChain = NULL; + +LExit: + ReleaseApupChain(pNewChain); + return hr; +} + + +// +// ApupFreeChain - frees a previously allocated application update chain. +// +extern "C" void DAPI ApupFreeChain( + __in APPLICATION_UPDATE_CHAIN* pChain + ) +{ + if (pChain) + { + for (DWORD i = 0; i < pChain->cEntries; ++i) + { + FreeEntry(pChain->rgEntries + i); + } + + ReleaseMem(pChain->rgEntries); + ReleaseStr(pChain->wzDefaultApplicationType); + ReleaseStr(pChain->wzDefaultApplicationId); + ReleaseMem(pChain); + } +} + + +static HRESULT ProcessEntry( + __in ATOM_ENTRY* pAtomEntry, + __in LPCWSTR wzDefaultAppId, + __out APPLICATION_UPDATE_ENTRY* pApupEntry + ) +{ + HRESULT hr = S_OK; + BOOL fVersionFound = FALSE; + + // First search the ATOM entry's custom elements to try and find the application update information. + for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) + { + hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0); + ExitOnFailure(hr, "Failed to allocate application identity."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) + { + hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0); + ExitOnFailure(hr, "Failed to allocate application type."); + } + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1)) + { + hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0); + ExitOnFailure(hr, "Failed to allocate upgrade id."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1)) + { + DWORD dwMajor = 0; + DWORD dwMinor = 0; + + hr = FileVersionFromString(pAttribute->wzValue, &dwMajor, &dwMinor); + ExitOnFailure(hr, "Failed to parse version string from ATOM entry."); + + pApupEntry->dw64UpgradeVersion = static_cast(dwMajor) << 32 | dwMinor; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzValue, -1, L"true", -1)) + { + pApupEntry->fUpgradeExclusive = TRUE; + } + } + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1)) + { + DWORD dwMajor = 0; + DWORD dwMinor = 0; + + hr = FileVersionFromString(pElement->wzValue, &dwMajor, &dwMinor); + ExitOnFailure(hr, "Failed to parse version string from ATOM entry."); + + pApupEntry->dw64Version = static_cast(dwMajor) << 32 | dwMinor; + fVersionFound = TRUE; + } + } + } + + // If there is no application identity or no version, skip the whole thing. + if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !fVersionFound) + { + ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version. + } + + if (pApupEntry->dw64UpgradeVersion >= pApupEntry->dw64Version) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); + } + + if (pAtomEntry->wzTitle) + { + hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0); + ExitOnFailure(hr, "Failed to allocate application title."); + } + + if (pAtomEntry->wzSummary) + { + hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0); + ExitOnFailure(hr, "Failed to allocate application summary."); + } + + if (pAtomEntry->pContent) + { + if (pAtomEntry->pContent->wzType) + { + hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0); + ExitOnFailure(hr, "Failed to allocate content type."); + } + + if (pAtomEntry->pContent->wzValue) + { + hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0); + ExitOnFailure(hr, "Failed to allocate content."); + } + } + // Now process the enclosures. Assume every link in the ATOM entry is an enclosure. + pApupEntry->rgEnclosures = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE)); + ExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry."); + + for (DWORD i = 0; i < pAtomEntry->cLinks; ++i) + { + ATOM_LINK* pLink = pAtomEntry->rgLinks + i; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1)) + { + hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures); + ExitOnFailure(hr, "Failed to parse enclosure."); + + pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures + + ++pApupEntry->cEnclosures; + } + } + +LExit: + if (S_OK != hr) // if anything went wrong, free the entry. + { + FreeEntry(pApupEntry); + memset(pApupEntry, 0, sizeof(APPLICATION_UPDATE_ENTRY)); + } + + return hr; +} + + +static HRESULT ParseEnclosure( + __in ATOM_LINK* pLink, + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ) +{ + HRESULT hr = S_OK; + + // First search the ATOM link's custom elements to try and find the application update enclosure information. + for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1)) + { + // Find the digest[@algorithm='sha256'] which is required. Everything else is ignored. + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1; + } + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256; + } + break; + } + } + + if (APUP_HASH_ALGORITHM_SHA256 == pEnclosure->digestAlgorithm) + { + if (64 != lstrlenW(pElement->wzValue)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Invalid digest length for SHA256 algorithm."); + } + + pEnclosure->cbDigest = sizeof(BYTE) * SHA256_DIGEST_LEN; + pEnclosure->rgbDigest = static_cast(MemAlloc(pEnclosure->cbDigest, TRUE)); + ExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); + + hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest); + ExitOnFailure(hr, "Failed to decode digest value."); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Unknown algorithm type for digest."); + } + + break; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1)) + { + hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0); + ExitOnFailure(hr, "Failed to copy local name."); + } + } + } + + pEnclosure->dw64Size = pLink->dw64Length; + + hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0); + ExitOnFailure(hr, "Failed to allocate enclosure URL."); + + pEnclosure->fInstaller = FALSE; + pEnclosure->wzLocalName = NULL; + +LExit: + return hr; +} + + +static __callback int __cdecl CompareEntries( + void* pvContext, + const void* pvLeft, + const void* pvRight + ) +{ + UNREFERENCED_PARAMETER(pvContext); + + int ret = 0; + const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast(pvLeft); + const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast(pvRight); + + if (pEntryLeft->dw64Version == pEntryRight->dw64Version) + { + if (pEntryLeft->dw64UpgradeVersion == pEntryRight->dw64UpgradeVersion) + { + ret = (pEntryRight->dw64TotalSize < pEntryLeft->dw64TotalSize) ? -1 : 1; + } + else + { + ret = (pEntryLeft->dw64UpgradeVersion > pEntryRight->dw64UpgradeVersion) ? -1 : 1; + } + } + else + { + ret = (pEntryLeft->dw64Version > pEntryRight->dw64Version) ? -1 : 1; + } + + return ret; +} + + +static HRESULT FilterEntries( + __in APPLICATION_UPDATE_ENTRY* rgEntries, + __in DWORD cEntries, + __in DWORD64 dw64CurrentVersion, + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, + __inout DWORD* pcFilteredEntries + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + const APPLICATION_UPDATE_ENTRY* pRequired = NULL;; + LPVOID pv = NULL; + + if (cEntries) + { + for (DWORD i = 0; i < cEntries; ++i) + { + const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i; + if (((pEntry->fUpgradeExclusive && dw64CurrentVersion > pEntry->dw64UpgradeVersion) || (!pEntry->fUpgradeExclusive && dw64CurrentVersion >= pEntry->dw64UpgradeVersion)) && + dw64CurrentVersion < pEntry->dw64Version) + { + pRequired = pEntry; + break; + } + } + + if (pRequired) + { + DWORD cNewFilteredEntries = *pcFilteredEntries + 1; + + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries); + + if (*prgFilteredEntries) + { + pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries."); + } + else + { + pv = MemAlloc(cbAllocSize, TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries."); + } + + *pcFilteredEntries = cNewFilteredEntries; + *prgFilteredEntries = static_cast(pv); + pv = NULL; + + hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1); + ExitOnFailure(hr, "Failed to deep copy entry."); + + if (pRequired->dw64Version < rgEntries[0].dw64Version) + { + FilterEntries(rgEntries, cEntries, pRequired->dw64Version, prgFilteredEntries, pcFilteredEntries); + } + } + } + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT CopyEntry( + __in const APPLICATION_UPDATE_ENTRY* pSrc, + __in APPLICATION_UPDATE_ENTRY* pDest + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENTRY)); + + if (pSrc->wzApplicationId) + { + hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0); + ExitOnFailure(hr, "Failed to copy application id."); + } + + if (pSrc->wzApplicationType) + { + hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0); + ExitOnFailure(hr, "Failed to copy application type."); + } + + if (pSrc->wzUpgradeId) + { + hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0); + ExitOnFailure(hr, "Failed to copy upgrade id."); + } + + if (pSrc->wzTitle) + { + hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0); + ExitOnFailure(hr, "Failed to copy title."); + } + + if (pSrc->wzSummary) + { + hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0); + ExitOnFailure(hr, "Failed to copy summary."); + } + + if (pSrc->wzContentType) + { + hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0); + ExitOnFailure(hr, "Failed to copy content type."); + } + + if (pSrc->wzContent) + { + hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0); + ExitOnFailure(hr, "Failed to copy content."); + } + + pDest->dw64TotalSize = pSrc->dw64TotalSize; + pDest->dw64UpgradeVersion = pSrc->dw64UpgradeVersion; + pDest->dw64Version = pSrc->dw64Version; + pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive; + + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize); + ExitOnRootFailure(hr, "Overflow while calculating memory allocation size"); + + pDest->rgEnclosures = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures."); + + pDest->cEnclosures = pSrc->cEnclosures; + + for (DWORD i = 0; i < pDest->cEnclosures; ++i) + { + hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i); + ExitOnFailure(hr, "Failed to copy enclosure."); + } + +LExit: + if (FAILED(hr)) + { + FreeEntry(pDest); + } + + return hr; +} + + +static HRESULT CopyEnclosure( + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, + __in APPLICATION_UPDATE_ENCLOSURE* pDest + ) +{ + HRESULT hr = S_OK; + + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENCLOSURE)); + + if (pSrc->wzUrl) + { + hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0); + ExitOnFailure(hr, "Failed copy url."); + } + + if (pSrc->wzLocalName) + { + hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0); + ExitOnFailure(hr, "Failed copy url."); + } + + pDest->rgbDigest = static_cast(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE)); + ExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest."); + + pDest->cbDigest = pSrc->cbDigest; + + memcpy_s(pDest->rgbDigest, sizeof(BYTE) * pDest->cbDigest, pSrc->rgbDigest, sizeof(BYTE) * pSrc->cbDigest); + + pDest->digestAlgorithm = pSrc->digestAlgorithm; + + pDest->dw64Size = pSrc->dw64Size; + pDest->fInstaller = pSrc->fInstaller; + +LExit: + if (FAILED(hr)) + { + FreeEnclosure(pDest); + } + + return hr; +} + + +static void FreeEntry( + __in APPLICATION_UPDATE_ENTRY* pEntry + ) +{ + if (pEntry) + { + for (DWORD i = 0; i < pEntry->cEnclosures; ++i) + { + FreeEnclosure(pEntry->rgEnclosures + i); + } + + ReleaseStr(pEntry->wzUpgradeId); + ReleaseStr(pEntry->wzApplicationType); + ReleaseStr(pEntry->wzApplicationId); + ReleaseStr(pEntry->wzTitle); + ReleaseStr(pEntry->wzSummary); + ReleaseStr(pEntry->wzContentType); + ReleaseStr(pEntry->wzContent); + } +} + + +static void FreeEnclosure( + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ) +{ + if (pEnclosure) + { + ReleaseMem(pEnclosure->rgbDigest); + ReleaseStr(pEnclosure->wzLocalName); + ReleaseStr(pEnclosure->wzUrl); + } +} diff --git a/src/dutil/atomutil.cpp b/src/dutil/atomutil.cpp new file mode 100644 index 00000000..4a12fb80 --- /dev/null +++ b/src/dutil/atomutil.cpp @@ -0,0 +1,1284 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static HRESULT ParseAtomDocument( + __in IXMLDOMDocument *pixd, + __out ATOM_FEED **ppFeed + ); +static HRESULT ParseAtomFeed( + __in IXMLDOMNode *pixnFeed, + __out ATOM_FEED **ppFeed + ); +static HRESULT ParseAtomAuthor( + __in IXMLDOMNode* pixnAuthor, + __in ATOM_AUTHOR* pAuthor + ); +static HRESULT ParseAtomCategory( + __in IXMLDOMNode* pixnCategory, + __in ATOM_CATEGORY* pCategory + ); +static HRESULT ParseAtomEntry( + __in IXMLDOMNode* pixnEntry, + __in ATOM_ENTRY* pEntry + ); +static HRESULT ParseAtomLink( + __in IXMLDOMNode* pixnLink, + __in ATOM_LINK* pLink + ); +static HRESULT ParseAtomUnknownElement( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement + ); +static HRESULT ParseAtomUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ); +static HRESULT AssignDateTime( + __in FILETIME* pft, + __in IXMLDOMNode* pNode + ); +static HRESULT AssignString( + __out_z LPWSTR* pwzValue, + __in IXMLDOMNode* pNode + ); +static void FreeAtomAuthor( + __in_opt ATOM_AUTHOR* pAuthor + ); +static void FreeAtomContent( + __in_opt ATOM_CONTENT* pContent + ); +static void FreeAtomCategory( + __in_opt ATOM_CATEGORY* pCategory + ); +static void FreeAtomEntry( + __in_opt ATOM_ENTRY* pEntry + ); +static void FreeAtomLink( + __in_opt ATOM_LINK* pLink + ); +static void FreeAtomUnknownElementList( + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement + ); +static void FreeAtomUnknownAttributeList( + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ); + +template static HRESULT AllocateAtomType( + __in IXMLDOMNode* pixnParent, + __in LPCWSTR wzT, + __out T** pprgT, + __out DWORD* pcT + ); + + +/******************************************************************** + AtomInitialize - Initialize ATOM utilities. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomInitialize() +{ + return XmlInitialize(); +} + + +/******************************************************************** + AtomUninitialize - Uninitialize ATOM utilities. + +*********************************************************************/ +extern "C" void DAPI AtomUninitialize() +{ + XmlUninitialize(); +} + + +/******************************************************************** + AtomParseFromString - parses out an ATOM feed from a string. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromString( + __in LPCWSTR wzAtomString, + __out ATOM_FEED **ppFeed + ) +{ + Assert(wzAtomString); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + IXMLDOMDocument *pixdAtom = NULL; + + hr = XmlLoadDocument(wzAtomString, &pixdAtom); + ExitOnFailure(hr, "Failed to load ATOM string as XML document."); + + hr = ParseAtomDocument(pixdAtom, &pNewFeed); + ExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + ReleaseObject(pixdAtom); + + return hr; +} + + +/******************************************************************** + AtomParseFromFile - parses out an ATOM feed from a file path. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromFile( + __in LPCWSTR wzAtomFile, + __out ATOM_FEED **ppFeed + ) +{ + Assert(wzAtomFile); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + IXMLDOMDocument *pixdAtom = NULL; + + hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom); + ExitOnFailure(hr, "Failed to load ATOM string as XML document."); + + hr = ParseAtomDocument(pixdAtom, &pNewFeed); + ExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + ReleaseObject(pixdAtom); + + return hr; +} + + +/******************************************************************** + AtomParseFromDocument - parses out an ATOM feed from an XML document. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromDocument( + __in IXMLDOMDocument* pixdDocument, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixdDocument); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + + hr = ParseAtomDocument(pixdDocument, &pNewFeed); + ExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + AtomFreeFeed - parses out an ATOM feed from a string. + +*********************************************************************/ +extern "C" void DAPI AtomFreeFeed( + __in_xcount(pFeed->cItems) ATOM_FEED *pFeed + ) +{ + if (pFeed) + { + FreeAtomUnknownElementList(pFeed->pUnknownElements); + ReleaseObject(pFeed->pixn); + + for (DWORD i = 0; i < pFeed->cLinks; ++i) + { + FreeAtomLink(pFeed->rgLinks + i); + } + ReleaseMem(pFeed->rgLinks); + + for (DWORD i = 0; i < pFeed->cEntries; ++i) + { + FreeAtomEntry(pFeed->rgEntries + i); + } + ReleaseMem(pFeed->rgEntries); + + for (DWORD i = 0; i < pFeed->cCategories; ++i) + { + FreeAtomCategory(pFeed->rgCategories + i); + } + ReleaseMem(pFeed->rgCategories); + + for (DWORD i = 0; i < pFeed->cAuthors; ++i) + { + FreeAtomAuthor(pFeed->rgAuthors + i); + } + ReleaseMem(pFeed->rgAuthors); + + ReleaseStr(pFeed->wzGenerator); + ReleaseStr(pFeed->wzIcon); + ReleaseStr(pFeed->wzId); + ReleaseStr(pFeed->wzLogo); + ReleaseStr(pFeed->wzSubtitle); + ReleaseStr(pFeed->wzTitle); + + MemFree(pFeed); + } +} + + +/******************************************************************** + ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document. + +*********************************************************************/ +static HRESULT ParseAtomDocument( + __in IXMLDOMDocument *pixd, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixd); + Assert(ppFeed); + + HRESULT hr = S_OK; + IXMLDOMElement *pFeedElement = NULL; + + ATOM_FEED *pNewFeed = NULL; + + // + // Get the document element and start processing feeds. + // + hr = pixd->get_documentElement(&pFeedElement); + ExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); + + hr = ParseAtomFeed(pFeedElement, &pNewFeed); + ExitOnFailure(hr, "Failed to parse ATOM feed."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseObject(pFeedElement); + + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element. + +*********************************************************************/ +static HRESULT ParseAtomFeed( + __in IXMLDOMNode *pixnFeed, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixnFeed); + Assert(ppFeed); + + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + ATOM_FEED *pNewFeed = NULL; + DWORD cAuthors = 0; + DWORD cCategories = 0; + DWORD cEntries = 0; + DWORD cLinks = 0; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // First, allocate the new feed and all the possible sub elements. + pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE); + ExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); + + pNewFeed->pixn = pixnFeed; + pNewFeed->pixn->AddRef(); + + hr = AllocateAtomType(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors); + ExitOnFailure(hr, "Failed to allocate ATOM feed authors."); + + hr = AllocateAtomType(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories); + ExitOnFailure(hr, "Failed to allocate ATOM feed categories."); + + hr = AllocateAtomType(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries); + ExitOnFailure(hr, "Failed to allocate ATOM feed entries."); + + hr = AllocateAtomType(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks); + ExitOnFailure(hr, "Failed to allocate ATOM feed links."); + + // Second, process the elements under a feed. + hr = pixnFeed->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1)) + { + hr = AssignString(&pNewFeed->wzGenerator, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM feed generator."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1)) + { + hr = AssignString(&pNewFeed->wzIcon, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM feed icon."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) + { + hr = AssignString(&pNewFeed->wzId, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM feed id."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1)) + { + hr = AssignString(&pNewFeed->wzLogo, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM feed logo."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1)) + { + hr = AssignString(&pNewFeed->wzSubtitle, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pNewFeed->wzTitle, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM feed title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) + { + hr = AssignDateTime(&pNewFeed->ftUpdated, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM feed updated."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) + { + hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]); + ExitOnFailure(hr, "Failed to parse ATOM author."); + + ++cAuthors; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) + { + hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]); + ExitOnFailure(hr, "Failed to parse ATOM category."); + + ++cCategories; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1)) + { + hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]); + ExitOnFailure(hr, "Failed to parse ATOM entry."); + + ++cEntries; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) + { + hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]); + ExitOnFailure(hr, "Failed to parse ATOM link."); + + ++cLinks; + } + else + { + hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements); + ExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + if (!pNewFeed->wzId || !*pNewFeed->wzId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find required feed/id element."); + } + else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find required feed/title element."); + } + else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find required feed/updated element."); + } + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + AllocateAtomType - allocates enough space for all of the ATOM elements + of a particular type under a particular node. + +*********************************************************************/ +template static HRESULT AllocateAtomType( + __in IXMLDOMNode* pixnParent, + __in LPCWSTR wzT, + __out T** pprgT, + __out DWORD* pcT + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + long cT = 0; + T* prgT = NULL; + + hr = XmlSelectNodes(pixnParent, wzT, &pNodeList); + ExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); + + if (S_OK == hr) + { + hr = pNodeList->get_length(&cT); + ExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); + + if (cT == 0) + { + ExitFunction(); + } + + prgT = static_cast(MemAlloc(sizeof(T) * cT, TRUE)); + ExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); + + *pcT = cT; + *pprgT = prgT; + prgT = NULL; + } + else + { + *pprgT = NULL; + *pcT = 0; + } + +LExit: + ReleaseMem(prgT); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomAuthor( + __in IXMLDOMNode* pixnAuthor, + __in ATOM_AUTHOR* pAuthor + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + hr = pixnAuthor->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1)) + { + hr = AssignString(&pAuthor->wzName, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM author name."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1)) + { + hr = AssignString(&pAuthor->wzEmail, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM author email."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1)) + { + hr = AssignString(&pAuthor->wzUrl, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM author uri."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM author elements."); + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomCategory( + __in IXMLDOMNode* pixnCategory, + __in ATOM_CATEGORY* pCategory + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnCategory->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1)) + { + hr = AssignString(&pCategory->wzLabel, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM category label."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1)) + { + hr = AssignString(&pCategory->wzScheme, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM category scheme."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1)) + { + hr = AssignString(&pCategory->wzTerm, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM category term."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM category attributes."); + + // Process elements second. + hr = pixnCategory->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements); + ExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM category elements."); + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomContent - parses out an ATOM content from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomContent( + __in IXMLDOMNode* pixnContent, + __in ATOM_CONTENT* pContent + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnContent->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) + { + hr = AssignString(&pContent->wzType, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM content type."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1)) + { + hr = AssignString(&pContent->wzUrl, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM content scheme."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM content attributes."); + + // Process elements second. + hr = pixnContent->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements); + ExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM content elements."); + + hr = AssignString(&pContent->wzValue, pixnContent); + ExitOnFailure(hr, "Failed to allocate ATOM content value."); + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomEntry( + __in IXMLDOMNode* pixnEntry, + __in ATOM_ENTRY* pEntry + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + DWORD cAuthors = 0; + DWORD cCategories = 0; + DWORD cLinks = 0; + + pEntry->pixn = pixnEntry; + pEntry->pixn->AddRef(); + + // First, allocate all the possible sub elements. + hr = AllocateAtomType(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors); + ExitOnFailure(hr, "Failed to allocate ATOM entry authors."); + + hr = AllocateAtomType(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories); + ExitOnFailure(hr, "Failed to allocate ATOM entry categories."); + + hr = AllocateAtomType(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks); + ExitOnFailure(hr, "Failed to allocate ATOM entry links."); + + // Second, process elements. + hr = pixnEntry->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) + { + hr = AssignString(&pEntry->wzId, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM entry id."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1)) + { + hr = AssignString(&pEntry->wzSummary, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM entry summary."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pEntry->wzTitle, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM entry title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1)) + { + hr = AssignDateTime(&pEntry->ftPublished, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM entry published."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) + { + hr = AssignDateTime(&pEntry->ftUpdated, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM entry updated."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) + { + hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]); + ExitOnFailure(hr, "Failed to parse ATOM entry author."); + + ++cAuthors; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) + { + hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]); + ExitOnFailure(hr, "Failed to parse ATOM entry category."); + + ++cCategories; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1)) + { + if (NULL != pEntry->pContent) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); + } + + pEntry->pContent = static_cast(MemAlloc(sizeof(ATOM_CONTENT), TRUE)); + ExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); + + hr = ParseAtomContent(pNode, pEntry->pContent); + ExitOnFailure(hr, "Failed to parse ATOM entry content."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) + { + hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]); + ExitOnFailure(hr, "Failed to parse ATOM entry link."); + + ++cLinks; + } + else + { + hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements); + ExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM entry elements."); + + if (!pEntry->wzId || !*pEntry->wzId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); + } + else if (!pEntry->wzTitle || !*pEntry->wzTitle) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); + } + else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); + } + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomLink - parses out an ATOM link from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomLink( + __in IXMLDOMNode* pixnLink, + __in ATOM_LINK* pLink + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnLink->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "Failed get attributes for ATOM link."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1)) + { + hr = AssignString(&pLink->wzRel, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM link rel."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1)) + { + hr = AssignString(&pLink->wzUrl, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM link href."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1)) + { + hr = XmlGetAttributeLargeNumber(pixnLink, bstrNodeName, &pLink->dw64Length); + if (E_INVALIDARG == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to parse ATOM link length."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pLink->wzTitle, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM link title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) + { + hr = AssignString(&pLink->wzType, pNode); + ExitOnFailure(hr, "Failed to allocate ATOM link type."); + } + else + { + hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes); + ExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM link attributes."); + + // Process elements second. + hr = pixnLink->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements); + ExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + ExitOnFailure(hr, "Failed to process all ATOM link elements."); + + hr = AssignString(&pLink->wzValue, pixnLink); + ExitOnFailure(hr, "Failed to allocate ATOM link value."); + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomUnknownElement( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement + ) +{ + Assert(ppUnknownElement); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + ATOM_UNKNOWN_ELEMENT* pNewUnknownElement; + + pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE); + ExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); + ExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get unknown element namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + ExitOnFailure(hr, "Failed to get unknown element name."); + + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); + ExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get unknown element value."); + + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); + + hr = pNode->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) + { + hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); + ExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); + + ReleaseNullObject(pixnAttribute); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); + + ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownElement; + pNewUnknownElement = NULL; + +LExit: + FreeAtomUnknownElementList(pNewUnknownElement); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + + return hr; +} + + +/******************************************************************** + ParseAtomUnknownAttribute - parses out attribute from an unknown element + +*********************************************************************/ +static HRESULT ParseAtomUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ) +{ + Assert(ppUnknownAttribute); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; + + pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE); + ExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); + ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get unknown attribute namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + ExitOnFailure(hr, "Failed to get unknown attribute name."); + + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); + ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get unknown attribute value."); + + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); + + ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownAttribute; + pNewUnknownAttribute = NULL; + +LExit: + FreeAtomUnknownAttributeList(pNewUnknownAttribute); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + + return hr; +} + + +/******************************************************************** + AssignDateTime - assigns the value of a node to a FILETIME struct. + +*********************************************************************/ +static HRESULT AssignDateTime( + __in FILETIME* pft, + __in IXMLDOMNode* pNode + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Already process this datetime value."); + } + + hr = XmlGetText(pNode, &bstrValue); + ExitOnFailure(hr, "Failed to get value."); + + if (S_OK == hr) + { + hr = TimeFromString3339(bstrValue, pft); + ExitOnFailure(hr, "Failed to convert value to time."); + } + else + { + ZeroMemory(pft, sizeof(FILETIME)); + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrValue); + + return hr; +} + + +/******************************************************************** + AssignString - assigns the value of a node to a dynamic string. + +*********************************************************************/ +static HRESULT AssignString( + __out_z LPWSTR* pwzValue, + __in IXMLDOMNode* pNode + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + if (pwzValue && *pwzValue) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Already processed this value."); + } + + hr = XmlGetText(pNode, &bstrValue); + ExitOnFailure(hr, "Failed to get value."); + + if (S_OK == hr) + { + hr = StrAllocString(pwzValue, bstrValue, 0); + ExitOnFailure(hr, "Failed to allocate value."); + } + else + { + ReleaseNullStr(pwzValue); + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrValue); + + return hr; +} + + +/******************************************************************** + FreeAtomAuthor - releases all of the memory used by an ATOM author. + +*********************************************************************/ +static void FreeAtomAuthor( + __in_opt ATOM_AUTHOR* pAuthor + ) +{ + if (pAuthor) + { + ReleaseStr(pAuthor->wzUrl); + ReleaseStr(pAuthor->wzEmail); + ReleaseStr(pAuthor->wzName); + } +} + + +/******************************************************************** + FreeAtomCategory - releases all of the memory used by an ATOM category. + +*********************************************************************/ +static void FreeAtomCategory( + __in_opt ATOM_CATEGORY* pCategory + ) +{ + if (pCategory) + { + FreeAtomUnknownElementList(pCategory->pUnknownElements); + + ReleaseStr(pCategory->wzTerm); + ReleaseStr(pCategory->wzScheme); + ReleaseStr(pCategory->wzLabel); + } +} + + +/******************************************************************** + FreeAtomContent - releases all of the memory used by an ATOM content. + +*********************************************************************/ +static void FreeAtomContent( + __in_opt ATOM_CONTENT* pContent + ) +{ + if (pContent) + { + FreeAtomUnknownElementList(pContent->pUnknownElements); + + ReleaseStr(pContent->wzValue); + ReleaseStr(pContent->wzUrl); + ReleaseStr(pContent->wzType); + } +} + + +/******************************************************************** + FreeAtomEntry - releases all of the memory used by an ATOM entry. + +*********************************************************************/ +static void FreeAtomEntry( + __in_opt ATOM_ENTRY* pEntry + ) +{ + if (pEntry) + { + FreeAtomUnknownElementList(pEntry->pUnknownElements); + ReleaseObject(pEntry->pixn); + + for (DWORD i = 0; i < pEntry->cLinks; ++i) + { + FreeAtomLink(pEntry->rgLinks + i); + } + ReleaseMem(pEntry->rgLinks); + + for (DWORD i = 0; i < pEntry->cCategories; ++i) + { + FreeAtomCategory(pEntry->rgCategories + i); + } + ReleaseMem(pEntry->rgCategories); + + for (DWORD i = 0; i < pEntry->cAuthors; ++i) + { + FreeAtomAuthor(pEntry->rgAuthors + i); + } + ReleaseMem(pEntry->rgAuthors); + + FreeAtomContent(pEntry->pContent); + ReleaseMem(pEntry->pContent); + + ReleaseStr(pEntry->wzTitle); + ReleaseStr(pEntry->wzSummary); + ReleaseStr(pEntry->wzId); + } +} + + +/******************************************************************** + FreeAtomLink - releases all of the memory used by an ATOM link. + +*********************************************************************/ +static void FreeAtomLink( + __in_opt ATOM_LINK* pLink + ) +{ + if (pLink) + { + FreeAtomUnknownElementList(pLink->pUnknownElements); + FreeAtomUnknownAttributeList(pLink->pUnknownAttributes); + + ReleaseStr(pLink->wzValue); + ReleaseStr(pLink->wzUrl); + ReleaseStr(pLink->wzType); + ReleaseStr(pLink->wzTitle); + ReleaseStr(pLink->wzRel); + } +} + + +/******************************************************************** + FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements + +*********************************************************************/ +static void FreeAtomUnknownElementList( + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement + ) +{ + while (pUnknownElement) + { + ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement; + pUnknownElement = pUnknownElement->pNext; + + FreeAtomUnknownAttributeList(pFree->pAttributes); + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzElement); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} + + +/******************************************************************** + FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes + +*********************************************************************/ +static void FreeAtomUnknownAttributeList( + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ) +{ + while (pUnknownAttribute) + { + ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; + pUnknownAttribute = pUnknownAttribute->pNext; + + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzAttribute); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp new file mode 100644 index 00000000..0cc67dcb --- /dev/null +++ b/src/dutil/buffutil.cpp @@ -0,0 +1,419 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +#define BUFFER_INCREMENT 128 + + +// helper function declarations + +static HRESULT EnsureBufferSize( + __deref_out_bcount(cbSize) BYTE** ppbBuffer, + __in SIZE_T cbSize + ); + + +// functions + +extern "C" HRESULT BuffReadNumber( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD* pdw + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD) > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw = *(const DWORD*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadNumber64( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD64* pdw64 + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw64); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD64) > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD64); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadString( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPWSTR* pscz + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pscz); + + HRESULT hr = S_OK; + DWORD cch = 0; + DWORD cb = 0; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + + // verify buffer size + if (sizeof(DWORD) > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small."); + } + + // read character count + cch = *(const DWORD*)(pbBuffer + *piBuffer); + + hr = ::DWordMult(cch, static_cast(sizeof(WCHAR)), &cb); + ExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + + hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer); + ExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small to hold character data."); + } + + // copy character data + hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch); + ExitOnFailure(hr, "Failed to copy character data."); + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadStringAnsi( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPSTR* pscz + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pscz); + + HRESULT hr = S_OK; + DWORD cch = 0; + DWORD cb = 0; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + + // verify buffer size + if (sizeof(DWORD) > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small."); + } + + // read character count + cch = *(const DWORD*)(pbBuffer + *piBuffer); + + hr = ::DWordMult(cch, static_cast(sizeof(CHAR)), &cb); + ExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + + hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer); + ExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small to hold character count."); + } + + // copy character data + hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch); + ExitOnFailure(hr, "Failed to copy character data."); + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadStream( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_bcount(*pcbStream) BYTE** ppbStream, + __out SIZE_T* pcbStream + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(ppbStream); + Assert(pcbStream); + + HRESULT hr = S_OK; + DWORD64 cb = 0; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); + + // verify buffer size + if (sizeof(DWORD64) > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small."); + } + + // read stream size + cb = *(const DWORD64*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD64); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small to hold byte count."); + } + + // allocate buffer + *ppbStream = (BYTE*)MemAlloc((SIZE_T)cb, TRUE); + ExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); + + // read stream data + memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, (SIZE_T)cb); + *piBuffer += (SIZE_T)cb; + + // return stream size + *pcbStream = (SIZE_T)cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteNumber( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD dw + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD)); + ExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD*)(*ppbBuffer + *piBuffer) = dw; + *piBuffer += sizeof(DWORD); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteNumber64( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD64 dw64 + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64)); + ExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64; + *piBuffer += sizeof(DWORD64); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteString( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCWSTR scz + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + DWORD cch = (DWORD)lstrlenW(scz); + SIZE_T cb = cch * sizeof(WCHAR); + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb)); + ExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy character count to buffer + *(DWORD*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(DWORD); + + // copy data to buffer + memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteStringAnsi( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCSTR scz + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + DWORD cch = (DWORD)lstrlenA(scz); + SIZE_T cb = cch * sizeof(CHAR); + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb)); + ExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy character count to buffer + *(DWORD*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(DWORD); + + // copy data to buffer + memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteStream( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_bcount(cbStream) const BYTE* pbStream, + __in SIZE_T cbStream + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + Assert(pbStream); + + HRESULT hr = S_OK; + DWORD64 cb = cbStream; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(DWORD64)); + ExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy byte count to buffer + *(DWORD64*)(*ppbBuffer + *piBuffer) = cb; + *piBuffer += sizeof(DWORD64); + + // copy data to buffer + memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream); + *piBuffer += cbStream; + +LExit: + return hr; +} + + +// helper functions + +static HRESULT EnsureBufferSize( + __deref_out_bcount(cbSize) BYTE** ppbBuffer, + __in SIZE_T cbSize + ) +{ + HRESULT hr = S_OK; + SIZE_T cbTarget = ((cbSize / BUFFER_INCREMENT) + 1) * BUFFER_INCREMENT; + + if (*ppbBuffer) + { + if (MemSize(*ppbBuffer) < cbTarget) + { + LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer."); + *ppbBuffer = (BYTE*)pv; + } + } + else + { + *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); + } + +LExit: + return hr; +} diff --git a/src/dutil/butil.cpp b/src/dutil/butil.cpp new file mode 100644 index 00000000..243befce --- /dev/null +++ b/src/dutil/butil.cpp @@ -0,0 +1,271 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include "butil.h" + +// constants +// From engine/registration.h +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; + +// Forward declarations. +static HRESULT OpenBundleKey( + __in LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key); + +/******************************************************************** +BundleGetBundleInfo - Queries the bundle installation metadata for a given property + +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) + The bundle is not installed + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) + The property is unrecognized + HRESULT_FROM_WIN32(ERROR_MORE_DATA) + A buffer is too small to hold the requested data. + E_NOTIMPL: + Tried to read a bundle attribute for a type which has not been implemented + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ +extern "C" HRESULT DAPI BundleGetBundleInfo( + __in LPCWSTR wzBundleId, + __in LPCWSTR wzAttribute, + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, + __inout_opt LPDWORD pcchValueBuf + ) +{ + Assert(wzBundleId && wzAttribute); + + HRESULT hr = S_OK; + BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE; + LPWSTR sczValue = NULL; + HKEY hkBundle = NULL; + DWORD cchSource = 0; + DWORD dwType = 0; + DWORD dwValue = 0; + + if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute) + { + ExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && + FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) + { + ExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); + } + + // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY + hr = RegGetType(hkBundle, wzAttribute, &dwType); + ExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, wzAttribute, &sczValue); + ExitOnFailure(hr, "Failed to read string property."); + break; + case REG_DWORD: + hr = RegReadNumber(hkBundle, wzAttribute, &dwValue); + ExitOnFailure(hr, "Failed to read dword property."); + + hr = StrAllocFormatted(&sczValue, L"%d", dwValue); + ExitOnFailure(hr, "Failed to format dword property as string."); + break; + default: + ExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); + + } + + hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + ExitOnFailure(hr, "Failed to calculate length of string"); + + if (lpValueBuf) + { + // cchSource is the length of the string not including the terminating null character + if (*pcchValueBuf <= cchSource) + { + *pcchValueBuf = ++cchSource; + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); + } + + hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + + *pcchValueBuf = cchSource++; + } + +LExit: + ReleaseRegKey(hkBundle); + ReleaseStr(sczValue); + + return hr; +} + +/******************************************************************** +BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code + +NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long. + The first 38 characters are for the GUID, and the last character is for the terminating null character. +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ +HRESULT DAPI BundleEnumRelatedBundle( + __in LPCWSTR wzUpgradeCode, + __in BUNDLE_INSTALL_CONTEXT context, + __inout PDWORD pdwStartIndex, + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + HKEY hkUninstall = NULL; + HKEY hkBundle = NULL; + LPWSTR sczUninstallSubKey = NULL; + DWORD cchUninstallSubKey = 0; + LPWSTR sczUninstallSubKeyPath = NULL; + LPWSTR sczValue = NULL; + DWORD dwType = 0; + + LPWSTR* rgsczBundleUpgradeCodes = NULL; + DWORD cBundleUpgradeCodes = 0; + BOOL fUpgradeCodeFound = FALSE; + + if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex) + { + ExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall); + ExitOnFailure(hr, "Failed to open bundle uninstall key path."); + + for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++) + { + hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey); + ExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); + + hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey); + ExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle); + ExitOnFailure(hr, "Failed to open uninstall key path."); + + // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ + hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); + if (FAILED(hr)) + { + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + // Not a bundle + continue; + } + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue); + ExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + + ReleaseNullStr(sczValue); + + break; + case REG_MULTI_SZ: + hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes); + ExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); + + for (DWORD i = 0; i < cBundleUpgradeCodes; i++) + { + LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i]; + if (wzBundleUpgradeCode && *wzBundleUpgradeCode) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + } + } + ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + break; + + default: + ExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); + + } + + if (fUpgradeCodeFound) + { + if (lpBundleIdBuf) + { + hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast(&cchUninstallSubKey)); + ExitOnFailure(hr, "Failed to calculate length of string"); + + hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + } + + break; + } + + // Cleanup before next iteration + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + } + +LExit: + ReleaseStr(sczValue); + ReleaseStr(sczUninstallSubKey); + ReleaseStr(sczUninstallSubKeyPath); + ReleaseRegKey(hkBundle); + ReleaseRegKey(hkUninstall); + ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + return hr; +} + +/******************************************************************** +OpenBundleKey - Opens the bundle uninstallation key for a given bundle + +NOTE: caller is responsible for closing key +********************************************************************/ +HRESULT OpenBundleKey( + __in LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key) +{ + Assert(key && wzBundleId); + AssertSz(NULL == *key, "*key should be null"); + + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + LPWSTR sczKeypath = NULL; + + hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); + ExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); + ExitOnFailure(hr, "Failed to open bundle uninstall key path."); + +LExit: + ReleaseStr(sczKeypath); + + return hr; +} diff --git a/src/dutil/cabcutil.cpp b/src/dutil/cabcutil.cpp new file mode 100644 index 00000000..35fefaba --- /dev/null +++ b/src/dutil/cabcutil.cpp @@ -0,0 +1,1532 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?'; +static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024; + +// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder() +// too often (because of duplicates too close together) we theoretically ruin our compression ratio - +// left at zero to maximize install-time performance, because even a small minimum threshhold seems to +// have a high install-time performance cost for little or no size benefit. The value is left here for +// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB. +static const DWORD MINFLUSHTHRESHHOLD = 0; + +// structs +struct MS_CABINET_HEADER +{ + DWORD sig; + DWORD csumHeader; + DWORD cbCabinet; + DWORD csumFolders; + DWORD coffFiles; + DWORD csumFiles; + WORD version; + WORD cFolders; + WORD cFiles; + WORD flags; + WORD setID; + WORD iCabinet; +}; + + +struct MS_CABINET_ITEM +{ + DWORD cbFile; + DWORD uoffFolderStart; + WORD iFolder; + WORD date; + WORD time; + WORD attribs; +}; + +struct CABC_INTERNAL_ADDFILEINFO +{ + LPCWSTR wzSourcePath; + LPCWSTR wzEmptyPath; +}; + +struct CABC_DUPLICATEFILE +{ + DWORD dwFileArrayIndex; + DWORD dwDuplicateCabFileIndex; + LPWSTR pwzSourcePath; + LPWSTR pwzToken; +}; + + +struct CABC_FILE +{ + DWORD dwCabFileIndex; + LPWSTR pwzSourcePath; + LPWSTR pwzToken; + PMSIFILEHASHINFO pmfHash; + LONGLONG llFileSize; + BOOL fHasDuplicates; +}; + + +struct CABC_DATA +{ + LONGLONG llBytesSinceLastFlush; + LONGLONG llFlushThreshhold; + + STRINGDICT_HANDLE shDictHandle; + + WCHAR wzCabinetPath[MAX_PATH]; + WCHAR wzEmptyFile[MAX_PATH]; + HANDLE hEmptyFile; + DWORD dwLastFileIndex; + + DWORD cFilePaths; + DWORD cMaxFilePaths; + CABC_FILE *prgFiles; + + DWORD cDuplicates; + DWORD cMaxDuplicates; + CABC_DUPLICATEFILE *prgDuplicates; + + HRESULT hrLastError; + BOOL fGoodCab; + + HFCI hfci; + ERF erf; + CCAB ccab; + TCOMP tc; + + // Below Field are used for Cabinet Splitting + BOOL fCabinetSplittingEnabled; + FileSplitCabNamesCallback fileSplitCabNamesCallback; + WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting +}; + +const int CABC_HANDLE_BYTES = sizeof(CABC_DATA); + +// +// prototypes +// +static void FreeCabCData( + __in CABC_DATA* pcd + ); +static HRESULT CheckForDuplicateFile( + __in CABC_DATA *pcd, + __out CABC_FILE **ppcf, + __in LPCWSTR wzFileName, + __in PMSIFILEHASHINFO *ppmfHash, + __in LONGLONG llFileSize + ); +static HRESULT AddDuplicateFile( + __in CABC_DATA *pcd, + __in DWORD dwFileArrayIndex, + __in_z LPCWSTR wzSourcePath, + __in_opt LPCWSTR wzToken, + __in DWORD dwDuplicateCabFileIndex + ); +static HRESULT AddNonDuplicateFile( + __in CABC_DATA *pcd, + __in LPCWSTR wzFile, + __in_opt LPCWSTR wzToken, + __in_opt const MSIFILEHASHINFO* pmfHash, + __in LONGLONG llFileSize, + __in DWORD dwCabFileIndex + ); +static HRESULT UpdateDuplicateFiles( + __in const CABC_DATA *pcd + ); +static HRESULT DuplicateFile( + __in MS_CABINET_HEADER *pHeader, + __in const CABC_DATA *pcd, + __in const CABC_DUPLICATEFILE *pDuplicate + ); +static HRESULT UtcFileTimeToLocalDosDateTime( + __in const FILETIME* pFileTime, + __out USHORT* pDate, + __out USHORT* pTime + ); + +static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb); +static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback INT_PTR DIAMONDAPI CabCOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback UINT FAR DIAMONDAPI CabCRead(__in INT_PTR hf, __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback UINT FAR DIAMONDAPI CabCWrite(__in INT_PTR hf, __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback long FAR DIAMONDAPI CabCSeek(__in INT_PTR hf, __in long dist, __in int seektype, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __out_bcount(CABC_HANDLE_BYTES) void *pv); +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __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); +static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __out_bcount(CABC_HANDLE_BYTES) void *pv); + + +/******************************************************************** +CabcBegin - begins creating a cabinet + +NOTE: phContext must be the same handle used in AddFile and Finish. + wzCabDir can be L"", but not NULL. + dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case. + +********************************************************************/ +extern "C" HRESULT DAPI CabCBegin( + __in_z LPCWSTR wzCab, + __in_z LPCWSTR wzCabDir, + __in DWORD dwMaxFiles, + __in DWORD dwMaxSize, + __in DWORD dwMaxThresh, + __in COMPRESSION_TYPE ct, + __out HANDLE *phContext + ) +{ + Assert(wzCab && *wzCab && phContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = NULL; + WCHAR wzTempPath[MAX_PATH] = { }; + + C_ASSERT(sizeof(MSIFILEHASHINFO) == 20); + + WCHAR wzPathBuffer [MAX_PATH] = L""; + size_t cchPathBuffer; + if (wzCabDir) + { + hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); + ExitOnFailure(hr, "Failed to get length of cab directory"); + + // Need room to terminate with L'\\' and L'\0' + if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); + } + + hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir); + ExitOnFailure(hr, "Failed to copy cab directory to buffer"); + + if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) + { + hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\"); + ExitOnFailure(hr, "Failed to cat \\ to end of buffer"); + ++cchPathBuffer; + } + } + + pcd = static_cast(MemAlloc(sizeof(CABC_DATA), TRUE)); + ExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure"); + + pcd->hrLastError = S_OK; + pcd->fGoodCab = TRUE; + pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD; + + pcd->hEmptyFile = INVALID_HANDLE_VALUE; + + pcd->fileSplitCabNamesCallback = NULL; + + if (NULL == dwMaxSize) + { + pcd->ccab.cb = CAB_MAX_SIZE; + pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired + } + else + { + pcd->ccab.cb = dwMaxSize * 1024 * 1024; + pcd->fCabinetSplittingEnabled = TRUE; + } + + if (0 == dwMaxThresh) + { + // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work. + pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16; + } + else + { + pcd->ccab.cbFolderThresh = dwMaxThresh; + } + + // Translate the compression type + if (COMPRESSION_TYPE_NONE == ct) + { + pcd->tc = tcompTYPE_NONE; + } + else if (COMPRESSION_TYPE_LOW == ct) + { + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO; + } + else if (COMPRESSION_TYPE_MEDIUM == ct) + { + pcd->tc = TCOMPfromLZXWindow(18); + } + else if (COMPRESSION_TYPE_HIGH == ct) + { + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI; + } + else if (COMPRESSION_TYPE_MSZIP == ct) + { + pcd->tc = tcompTYPE_MSZIP; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid compression type specified."); + } + + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL)) + { + ExitWithLastError(hr, "failed to convert cab name to multi-byte"); + } + + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) + { + ExitWithLastError(hr, "failed to convert cab dir to multi-byte"); + } + + // Remember the path to the cabinet. + hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); + ExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); + + hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab); + ExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); + + // Get the empty file to use as the blank marker for duplicates. + if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + ExitWithLastError(hr, "Failed to get temp path."); + } + + if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile)) + { + ExitWithLastError(hr, "Failed to create a temp file name."); + } + + // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) + // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst + // case is we'll leave a zero byte file behind in the temp folder. + 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); + + hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); + + // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files + if (1 > dwMaxFiles) + { + dwMaxFiles = 1; + } + + pcd->cMaxFilePaths = dwMaxFiles; + size_t cbFileAllocSize = 0; + + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize)); + ExitOnFailure(hr, "Maximum allocation exceeded on initialization."); + + pcd->prgFiles = static_cast(MemAlloc(cbFileAllocSize, TRUE)); + ExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files."); + + // Tell cabinet API about our configuration. + pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd); + if (NULL == pcd->hfci || pcd->erf.fError) + { + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + ExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + + pcd->fGoodCab = FALSE; + + ExitOnFailure(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? + } + + *phContext = pcd; + +LExit: + if (FAILED(hr) && pcd && pcd->hfci) + { + ::FCIDestroy(pcd->hfci); + } + + return hr; +} + + +/******************************************************************** +CabCNextCab - This will be useful when creating multiple cabs. +Haven't needed it yet. +********************************************************************/ +extern "C" HRESULT DAPI CabCNextCab( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + UNREFERENCED_PARAMETER(hContext); + // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls + return E_NOTIMPL; +} + + +/******************************************************************** +CabcAddFile - adds a file to a cabinet + +NOTE: hContext must be the same used in Begin and Finish +if wzToken is null, the file's original name is used within the cab +********************************************************************/ +extern "C" HRESULT DAPI CabCAddFile( + __in_z LPCWSTR wzFile, + __in_z_opt LPCWSTR wzToken, + __in_opt PMSIFILEHASHINFO pmfHash, + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + Assert(wzFile && *wzFile && hContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = reinterpret_cast(hContext); + CABC_FILE *pcfDuplicate = NULL; + LPWSTR sczUpperCaseFile = NULL; + LONGLONG llFileSize = 0; + PMSIFILEHASHINFO pmfLocalHash = pmfHash; + + hr = StrAllocString(&sczUpperCaseFile, wzFile, 0); + ExitOnFailure(hr, "Failed to allocate new string for file %ls", wzFile); + + // Modifies the string in-place + StrStringToUpper(sczUpperCaseFile); + + // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired + // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled + if(!pcd->fCabinetSplittingEnabled) + { + // Store file size, primarily used to determine which files to hash for duplicates + hr = FileSize(wzFile, &llFileSize); + ExitOnFailure(hr, "Failed to check size of file %ls", wzFile); + + hr = CheckForDuplicateFile(pcd, &pcfDuplicate, sczUpperCaseFile, &pmfLocalHash, llFileSize); + ExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); + } + + if (pcfDuplicate) // This will be null for smart cabbing case + { + DWORD index; + hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index); + ExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); + + hr = AddDuplicateFile(pcd, index, sczUpperCaseFile, wzToken, pcd->dwLastFileIndex); + ExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); + } + else + { + hr = AddNonDuplicateFile(pcd, sczUpperCaseFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); + ExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); + } + + ++pcd->dwLastFileIndex; + +LExit: + ReleaseStr(sczUpperCaseFile); + + // If we allocated a hash struct ourselves, free it + if (pmfHash != pmfLocalHash) + { + ReleaseMem(pmfLocalHash); + } + + return hr; +} + + +/******************************************************************** +CabcFinish - finishes making a cabinet + +NOTE: hContext must be the same used in Begin and AddFile +*********************************************************************/ +extern "C" HRESULT DAPI CabCFinish( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback + ) +{ + Assert(hContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = reinterpret_cast(hContext); + CABC_INTERNAL_ADDFILEINFO fileInfo = { }; + DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex + DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array + DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array + LPSTR pszFileToken = NULL; + LONGLONG llFileSize = 0; + + pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback; + + // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile() + // doing so at appropriate times results in install-time performance benefits in the case of duplicate files. + // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump + // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder + // (this is not the same as a regular folder, and is a concept unique to CABs). + + // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files + // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that + // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB + // to close the current folder, and create a new folder for the next file to be added. + + // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence. + // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to") + // 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) + BOOL fFlushBefore = FALSE; + BOOL fFlushAfter = FALSE; + + ReleaseDict(pcd->shDictHandle); + + // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added + for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex) + { + if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file + { + // Just a normal, non-duplicated file. We'll add it to the list for later checking of + // duplicates. + fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath; + fileInfo.wzEmptyPath = NULL; + + // Use the provided token, otherwise default to the source file name. + if (pcd->prgFiles[dwArrayFileIndex].pwzToken) + { + LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken; + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + ExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp); + } + else + { + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + ExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp); + } + + if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates) + { + fFlushBefore = TRUE; + } + + llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize; + + ++dwArrayFileIndex; // Increment into the non-duplicate array + } + else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file + { + // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space + // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero + // byte files to point at their duplicated file index. + // + // Notice that duplicate files are not added to the list of file paths because all duplicate + // files point at the same path (the empty file) so there is no point in tracking them with + // their path. + fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath; + fileInfo.wzEmptyPath = pcd->wzEmptyFile; + + // Use the provided token, otherwise default to the source file name. + if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken) + { + LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken; + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + ExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp); + } + else + { + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + ExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp); + } + + // 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 + if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) && + !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) && + dwArrayFileIndex < pcd->cFilePaths) + { + fFlushAfter = TRUE; + } + + // We're just adding a 0-byte file, so set it appropriately + llFileSize = 0; + + ++dwDupeArrayFileIndex; // Increment into the duplicate array + } + else // If it's neither duplicate nor non-duplicate, throw an error + { + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); + ExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered"); + } + + if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) + { + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) + { + ExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + pcd->llBytesSinceLastFlush = 0; + } + + pcd->llBytesSinceLastFlush += llFileSize; + + // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct + // through the pointer to an ANSI string. This is neccessary so we can smuggle through the + // path to the empty file (should this be a duplicate file). +#pragma prefast(push) +#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here + if (!::FCIAddFile(pcd->hfci, reinterpret_cast(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc)) +#pragma prefast(pop) + { + pcd->fGoodCab = FALSE; + + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + ExitWithLastError(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); + } + + ExitOnFailure(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? + } + + // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet + // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead + if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + ExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet."); + } + + if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) + { + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) + { + ExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + pcd->llBytesSinceLastFlush = 0; + } + + fFlushAfter = FALSE; + fFlushBefore = FALSE; + } + + if (!pcd->fGoodCab) + { + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + ExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %s", pcd->erf.erfOper, pcd->erf.erfType); + } + + ExitOnFailure(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %s", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS? + } + + // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs) + if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus)) + { + // If we have a last error, use that, otherwise return the useless error + hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL; + ExitOnFailure(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? + } + + if (pcd->fGoodCab && pcd->cDuplicates) + { + hr = UpdateDuplicateFiles(pcd); + ExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); + } + +LExit: + ::FCIDestroy(pcd->hfci); + FreeCabCData(pcd); + ReleaseNullStr(pszFileToken); + + return hr; +} + + +/******************************************************************** +CabCCancel - cancels making a cabinet + +NOTE: hContext must be the same used in Begin and AddFile +*********************************************************************/ +extern "C" void DAPI CabCCancel( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + Assert(hContext); + + CABC_DATA* pcd = reinterpret_cast(hContext); + ::FCIDestroy(pcd->hfci); + FreeCabCData(pcd); +} + + +// +// private +// + +static void FreeCabCData( + __in CABC_DATA* pcd + ) +{ + if (pcd) + { + ReleaseFileHandle(pcd->hEmptyFile); + + for (DWORD i = 0; i < pcd->cFilePaths; ++i) + { + ReleaseStr(pcd->prgFiles[i].pwzSourcePath); + ReleaseMem(pcd->prgFiles[i].pmfHash); + } + ReleaseMem(pcd->prgFiles); + ReleaseMem(pcd->prgDuplicates); + + ReleaseMem(pcd); + } +} + +/******************************************************************** + SmartCab functions + +********************************************************************/ + +static HRESULT CheckForDuplicateFile( + __in CABC_DATA *pcd, + __out CABC_FILE **ppcf, + __in LPCWSTR wzFileName, + __in PMSIFILEHASHINFO *ppmfHash, + __in LONGLONG llFileSize + ) +{ + DWORD i; + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file"); + ExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file"); + + *ppcf = NULL; // By default, we'll set our output to NULL + + hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast(ppcf)); + // If we found it in the hash of previously added source paths, return our match immediately + if (SUCCEEDED(hr)) + { + ExitFunction1(hr = S_OK); + } + else if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files"); + + for (i = 0; i < pcd->cFilePaths; ++i) + { + // If two files have the same size, use hashing to check if they're a match + if (llFileSize == pcd->prgFiles[i].llFileSize) + { + // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it + if (pcd->prgFiles[i].pmfHash == NULL) + { + pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + ExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash"); + + pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash); + ExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath); + } + + // If our own file hasn't yet been hashed, hash it + if (NULL == *ppmfHash) + { + *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + ExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash"); + + (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash); + ExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath); + } + + // If the two file hashes are both of the expected size, and they match, we've got a match, so return it! + if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize && + sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize && + pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] && + pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] && + pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] && + pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3]) + { + *ppcf = pcd->prgFiles + i; + ExitFunction1(hr = S_OK); + } + } + } + +LExit: + + return hr; +} + + +static HRESULT AddDuplicateFile( + __in CABC_DATA *pcd, + __in DWORD dwFileArrayIndex, + __in_z LPCWSTR wzSourcePath, + __in_opt LPCWSTR wzToken, + __in DWORD dwDuplicateCabFileIndex + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + + // Ensure there is enough memory to store this duplicate file index. + if (pcd->cDuplicates == pcd->cMaxDuplicates) + { + pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?) + size_t cbDuplicates = 0; + + hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates); + ExitOnFailure(hr, "Maximum allocation exceeded."); + + if (pcd->cDuplicates) + { + pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file."); + } + else + { + pv = MemAlloc(cbDuplicates, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file."); + } + + ZeroMemory(reinterpret_cast(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE)); + + pcd->prgDuplicates = static_cast(pv); + pv = NULL; + } + + // Store the duplicate file index. + pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex; + pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex; + pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates + + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0); + ExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath); + + if (wzToken && *wzToken) + { + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0); + ExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken); + } + + ++pcd->cDuplicates; + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT AddNonDuplicateFile( + __in CABC_DATA *pcd, + __in LPCWSTR wzFile, + __in_opt LPCWSTR wzToken, + __in_opt const MSIFILEHASHINFO* pmfHash, + __in LONGLONG llFileSize, + __in DWORD dwCabFileIndex + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + + // Ensure there is enough memory to store this file index. + if (pcd->cFilePaths == pcd->cMaxFilePaths) + { + pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?) + size_t cbFilePaths = 0; + + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths); + ExitOnFailure(hr, "Maximum allocation exceeded."); + + pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file."); + + ZeroMemory(reinterpret_cast(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE)); + + pcd->prgFiles = static_cast(pv); + pv = NULL; + } + + // Store the file index information. + // TODO: add this to a sorted list so we can do a binary search later. + CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths; + pcf->dwCabFileIndex = dwCabFileIndex; + pcf->llFileSize = llFileSize; + + if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize) + { + pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + ExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash"); + + pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + pcf->pmfHash->dwData[0] = pmfHash->dwData[0]; + pcf->pmfHash->dwData[1] = pmfHash->dwData[1]; + pcf->pmfHash->dwData[2] = pmfHash->dwData[2]; + pcf->pmfHash->dwData[3] = pmfHash->dwData[3]; + } + + hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0); + ExitOnFailure(hr, "Failed to copy file path: %ls", wzFile); + + if (wzToken && *wzToken) + { + hr = StrAllocString(&pcf->pwzToken, wzToken, 0); + ExitOnFailure(hr, "Failed to copy file token: %ls", wzToken); + } + + ++pcd->cFilePaths; + + hr = DictAddValue(pcd->shDictHandle, pcf); + ExitOnFailure(hr, "Failed to add file to dictionary of added files"); + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT UpdateDuplicateFiles( + __in const CABC_DATA *pcd + ) +{ + HRESULT hr = S_OK; + DWORD cbCabinet = 0; + LARGE_INTEGER liCabinetSize = { }; + HANDLE hCabinet = INVALID_HANDLE_VALUE; + HANDLE hCabinetMapping = NULL; + LPVOID pv = NULL; + MS_CABINET_HEADER *pCabinetHeader = NULL; + + hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hCabinet) + { + ExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); + } + + // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as + // the upper bound for the memory map. + if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) + { + ExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); + } + + if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) + { + cbCabinet = liCabinetSize.LowPart; + } + else + { + cbCabinet = MAX_CABINET_HEADER_SIZE; + } + + // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE + hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); + if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) + { + ExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); + } + + pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); + ExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); + + pCabinetHeader = static_cast(pv); + + for (DWORD i = 0; i < pcd->cDuplicates; ++i) + { + const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i; + + hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile); + ExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex); + } + +LExit: + if (pv) + { + ::UnmapViewOfFile(pv); + } + if (hCabinetMapping) + { + ::CloseHandle(hCabinetMapping); + } + ReleaseFileHandle(hCabinet); + + return hr; +} + + +static HRESULT DuplicateFile( + __in MS_CABINET_HEADER *pHeader, + __in const CABC_DATA *pcd, + __in const CABC_DUPLICATEFILE *pDuplicate + ) +{ + HRESULT hr = S_OK; + BYTE *pbHeader = reinterpret_cast(pHeader); + BYTE* pbItem = pbHeader + pHeader->coffFiles; + const MS_CABINET_ITEM *pOriginalItem = NULL; + MS_CABINET_ITEM *pDuplicateItem = NULL; + + if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex || + pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex || + pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex); + } + + // Step through each cabinet items until we get to the original + // file's index. Notice that the name of the cabinet item is + // appended to the end of the MS_CABINET_INFO, that's why we can't + // index straight to the data we want. + for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i) + { + LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; + } + + pOriginalItem = reinterpret_cast(pbItem); + + // Now pick up where we left off after the original file's index + // was found and loop until we find the duplicate file's index. + for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i) + { + LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; + } + + pDuplicateItem = reinterpret_cast(pbItem); + + if (0 != pDuplicateItem->cbFile) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile); + } + + pDuplicateItem->cbFile = pOriginalItem->cbFile; + pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart; + pDuplicateItem->iFolder = pOriginalItem->iFolder; + // Note: we do *not* duplicate the date/time and attributes metadata from + // the original item to the duplicate. The following lines are commented + // so people are not tempted to put them back. + //pDuplicateItem->date = pOriginalItem->date; + //pDuplicateItem->time = pOriginalItem->time; + //pDuplicateItem->attribs = pOriginalItem->attribs; + +LExit: + return hr; +} + + +static HRESULT UtcFileTimeToLocalDosDateTime( + __in const FILETIME* pFileTime, + __out USHORT* pDate, + __out USHORT* pTime + ) +{ + HRESULT hr = S_OK; + FILETIME ftLocal = { }; + + if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal)) + { + ExitWithLastError(hr, "Filed to convert file time to local file time."); + } + + if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime)) + { + ExitWithLastError(hr, "Filed to convert file time to DOS date time."); + } + +LExit: + return hr; +} + + +/******************************************************************** + FCI callback functions + +*********************************************************************/ +static __callback int DIAMONDAPI CabCFilePlaced( + __in PCCAB pccab, + __in_z PSTR szFile, + __in long cbFile, + __in BOOL fContinuation, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(pccab); + UNREFERENCED_PARAMETER(szFile); + UNREFERENCED_PARAMETER(cbFile); + UNREFERENCED_PARAMETER(fContinuation); + UNREFERENCED_PARAMETER(pv); + return 0; +} + + +static __callback void * DIAMONDAPI CabCAlloc( + __in ULONG cb + ) +{ + return MemAlloc(cb, FALSE); +} + + +static __callback void DIAMONDAPI CabCFree( + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + MemFree(pv); +} + +static __callback INT_PTR DIAMONDAPI CabCOpen( + __in_z PSTR pszFile, + __in int oflag, + __in int pmode, + __out int *err, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + INT_PTR pFile = -1; + DWORD dwAccess = 0; + DWORD dwDisposition = 0; + DWORD dwAttributes = 0; + + // + // Translate flags for CreateFile + // + if (oflag & _O_CREAT) + { + if (pmode == _S_IREAD) + dwAccess |= GENERIC_READ; + else if (pmode == _S_IWRITE) + dwAccess |= GENERIC_WRITE; + else if (pmode == (_S_IWRITE | _S_IREAD)) + dwAccess |= GENERIC_READ | GENERIC_WRITE; + + if (oflag & _O_SHORT_LIVED) + dwDisposition = FILE_ATTRIBUTE_TEMPORARY; + else if (oflag & _O_TEMPORARY) + dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE; + else if (oflag & _O_EXCL) + dwDisposition = CREATE_NEW; + } + if (oflag & _O_TRUNC) + dwDisposition = CREATE_ALWAYS; + + if (!dwAccess) + dwAccess = GENERIC_READ; + if (!dwDisposition) + dwDisposition = OPEN_EXISTING; + if (!dwAttributes) + dwAttributes = FILE_ATTRIBUTE_NORMAL; + + // Check to see if we were passed the magic character that says 'Unicode string follows'. + if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile) + { + pFile = reinterpret_cast(::CreateFileW(reinterpret_cast(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); + } + else + { +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + pFile = reinterpret_cast(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); +#pragma prefast(pop) + } + + if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) + { + ExitOnLastError(hr, "failed to open file: %s", pszFile); + } + +LExit: + if (FAILED(hr)) + pcd->hrLastError = *err = hr; + + return FAILED(hr) ? -1 : pFile; +} + + +static __callback UINT FAR DIAMONDAPI CabCRead( + __in INT_PTR hf, + __out_bcount(cb) void FAR *memory, + __in UINT cb, + __out int *err, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD cbRead = 0; + + ExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided"); + if (!::ReadFile(reinterpret_cast(hf), memory, cb, &cbRead, NULL)) + { + *err = ::GetLastError(); + ExitOnLastError(hr, "failed to read during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI CabCWrite( + __in INT_PTR hf, + __in_bcount(cb) void FAR *memory, + __in UINT cb, + __out int *err, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + ExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided"); + if (!::WriteFile(reinterpret_cast(hf), memory, cb, &cbWrite, NULL)) + { + *err = ::GetLastError(); + ExitOnLastError(hr, "failed to write during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + pcd->hrLastError = *err = hr; + + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI CabCSeek( + __in INT_PTR hf, + __in long dist, + __in int seektype, + __out int *err, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + ExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype); + } + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + // Todo: update these comments for FCI (are they accurate for FCI as well?) + lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); + if (DWORD_MAX == lMove) + { + *err = ::GetLastError(); + ExitOnLastError(hr, "failed to move file pointer %d bytes", dist); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : lMove; +} + + +static __callback int FAR DIAMONDAPI CabCClose( + __in INT_PTR hf, + __out int *err, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + + if (!::CloseHandle(reinterpret_cast(hf))) + { + *err = ::GetLastError(); + ExitOnLastError(hr, "failed to close file during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : 0; +} + +static __callback int DIAMONDAPI CabCDelete( + __in_z PSTR szFile, + __out int *err, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(err); + UNREFERENCED_PARAMETER(pv); + +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + ::DeleteFileA(szFile); +#pragma prefast(pop) + + return 0; +} + + +__success(return != FALSE) +static __callback BOOL DIAMONDAPI CabCGetTempFile( + __out_bcount_z(cbFile) char *szFile, + __in int cbFile, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + static volatile DWORD dwIndex = 0; + + HRESULT hr = S_OK; + char szTempPath[MAX_PATH] = { }; + DWORD cchTempPath = MAX_PATH; + DWORD dwProcessId = ::GetCurrentProcessId(); + HANDLE hTempFile = INVALID_HANDLE_VALUE; + + if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) + { + ExitWithLastError(hr, "Failed to get temp path during cabinet creation."); + } + + for (DWORD i = 0; i < DWORD_MAX; ++i) + { + LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast(&dwIndex)); + + hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); + ExitOnFailure(hr, "failed to format log file path."); + + hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (INVALID_HANDLE_VALUE != hTempFile) + { + // we found one that doesn't exist + hr = S_OK; + break; + } + else + { + hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. + } + } + ExitOnFailure(hr, "failed to find temporary file."); + +LExit: + ReleaseFileHandle(hTempFile); + + if (FAILED(hr)) + { + pcd->hrLastError = hr; + } + + return FAILED(hr)? FALSE : TRUE; +} + + +__success(return != FALSE) +static __callback BOOL DIAMONDAPI CabCGetNextCabinet( + __in PCCAB pccab, + __in ULONG ul, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(ul); + + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + LPWSTR pwzFileToken = NULL; + WCHAR wzNewCabName[MAX_PATH] = L""; + + if (pccab->iCab == 1) + { + pcd->wzFirstCabinetName[0] = '\0'; + LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath); + size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName)); + if (len > 4) + { + len -= 4; // remove Extention ".cab" of 8.3 Format + } + hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len); + ExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name"); + } + + const int nAlphabets = 26; // Number of Alphabets from a to z + if (pccab->iCab <= nAlphabets) + { + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab)); + ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab)); + ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + } + else if (pccab->iCab <= nAlphabets*nAlphabets) + { + // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ...... + int char2 = (pccab->iCab) % nAlphabets; + int char1 = (pccab->iCab - char2)/nAlphabets; + if (char2 == 0) + { + // e.g. when iCab = 52, we want az + char2 = nAlphabets; // Second char must be 'z' in this case + char1--; // First Char must be decremented by 1 + } + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2)); + ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2)); + ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + } + else + { + hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index. + ExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name"); + } + + // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method + if(pcd->fileSplitCabNamesCallback != 0) + { + // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split + // This code will need updation if we need to send all file tokens for the splitting Cabinets + if (pcd->prgFiles[0].pwzToken) + { + pwzFileToken = pcd->prgFiles[0].pwzToken; + } + else + { + LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath; + pwzFileToken = FileFromPath(wzSourcePath); + } + + // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table + pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken); + } + +LExit: + if (FAILED(hr)) + { + // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!! + // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting + pcd->hrLastError = hr; + return FALSE; + } + else + { + return TRUE; + } +} + + +static __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 + ) +{ + HRESULT hr = S_OK; + CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast(pszName); + LPCWSTR wzFile = NULL; + DWORD cbFile = 0; + LPSTR pszFilePlusMagic = NULL; + DWORD cbFilePlusMagic = 0; + WIN32_FILE_ATTRIBUTE_DATA fad = { }; + INT_PTR iResult = -1; + + // If there is an empty file provided, use that as the source path to cab (since we + // must be dealing with a duplicate file). Otherwise, use the source path you'd expect. + wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath; + cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR); + + // Convert the source file path into an Ansi string that our APIs will recognize as + // a Unicode string (due to the magic character). + cbFilePlusMagic = cbFile + 1; // add one for the magic. + pszFilePlusMagic = reinterpret_cast(MemAlloc(cbFilePlusMagic, TRUE)); + + *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER; + memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile); + + if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad)) + { + ExitWithLastError(hr, "Failed to get file attributes on '%s'.", pFileInfo->wzSourcePath); + } + + // Set the attributes but only allow the few attributes that CAB supports. + *pattribs = static_cast(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE); + + hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime); + if (FAILED(hr)) + { + // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were + // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't + // found. This would create further problems if the file was written to the CAB without this value. Windows + // Installer would then fail to extract the file. + hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime); + ExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName); + } + + iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv); + +LExit: + ReleaseMem(pszFilePlusMagic); + if (FAILED(hr)) + { + *err = (int)hr; + } + + return FAILED(hr) ? -1 : iResult; +} + + +static __callback long DIAMONDAPI CabCStatus( + __in UINT ui, + __in ULONG cb1, + __in ULONG cb2, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(ui); + UNREFERENCED_PARAMETER(cb1); + UNREFERENCED_PARAMETER(cb2); + UNREFERENCED_PARAMETER(pv); + return 0; +} diff --git a/src/dutil/cabutil.cpp b/src/dutil/cabutil.cpp new file mode 100644 index 00000000..e0efb717 --- /dev/null +++ b/src/dutil/cabutil.cpp @@ -0,0 +1,567 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// external prototypes +typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*); +typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); +typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO); +typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *); + + +// +// static globals +// +static HMODULE vhCabinetDll = NULL; + +static HFDI vhfdi = NULL; +static PFNFDICREATE vpfnFDICreate = NULL; +static PFNFDICOPY vpfnFDICopy = NULL; +static PFNFDIISCABINET vpfnFDIIsCabinet = NULL; +static PFNFDIDESTROY vpfnFDIDestroy = NULL; +static ERF verf; + +static DWORD64 vdw64EmbeddedOffset = 0; + +// +// structs +// +struct CAB_CALLBACK_STRUCT +{ + BOOL fStopExtracting; // flag set when no more files are needed + LPCWSTR pwzExtract; // file to extract ("*" means extract all) + LPCWSTR pwzExtractDir; // directory to extract files to + + // possible user data + CAB_CALLBACK_PROGRESS pfnProgress; + LPVOID pvContext; +}; + +// +// prototypes +// +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize); +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData); +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode); +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb); +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb); +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf); +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype); +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify); +static 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); + +static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL; + + +inline HRESULT LoadCabinetDll() +{ + HRESULT hr = S_OK; + if (!vhCabinetDll) + { + hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll); + ExitOnFailure(hr, "failed to load cabinet.dll"); + + // retrieve all address functions + vpfnFDICreate = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICreate")); + ExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL"); + vpfnFDICopy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICopy")); + ExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL"); + vpfnFDIIsCabinet = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIIsCabinet")); + ExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL"); + vpfnFDIDestroy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIDestroy")); + ExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL"); + + vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf); + ExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll"); + } + +LExit: + if (FAILED(hr) && vhCabinetDll) + { + ::FreeLibrary(vhCabinetDll); + vhCabinetDll = NULL; + } + + return hr; +} + + +/******************************************************************** + CabInitialize - initializes internal static variables + +********************************************************************/ +extern "C" HRESULT DAPI CabInitialize( + __in BOOL fDelayLoad + ) +{ + HRESULT hr = S_OK; + + if (!fDelayLoad) + { + hr = LoadCabinetDll(); + ExitOnFailure(hr, "failed to load CABINET.DLL"); + } + +LExit: + return hr; +} + + +/******************************************************************** + CabUninitialize - initializes internal static variables + +********************************************************************/ +extern "C" void DAPI CabUninitialize( + ) +{ + if (vhfdi) + { + if (vpfnFDIDestroy) + { + vpfnFDIDestroy(vhfdi); + } + vhfdi = NULL; + } + + vpfnFDICreate = NULL; + vpfnFDICopy =NULL; + vpfnFDIIsCabinet = NULL; + vpfnFDIDestroy = NULL; + + if (vhCabinetDll) + { + ::FreeLibrary(vhCabinetDll); + vhCabinetDll = NULL; + } +} + +/******************************************************************** + CabEnumerate - list files inside cabinet + + NOTE: wzCabinet must be full path to cabinet file + pfnNotify is callback function to get notified for each file + in the cabinet +********************************************************************/ +extern "C" HRESULT DAPI CabEnumerate( + __in LPCWSTR wzCabinet, + __in LPCWSTR wzEnumerateFile, + __in STDCALL_PFNFDINOTIFY pfnNotify, + __in DWORD64 dw64EmbeddedOffset + ) +{ + return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset); +} + +/******************************************************************** + CabExtract - extracts one or all files from a cabinet + + NOTE: wzCabinet must be full path to cabinet file + wzExtractFile can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa +********************************************************************/ +extern "C" HRESULT DAPI CabExtract( + __in LPCWSTR wzCabinet, + __in LPCWSTR wzExtractFile, + __in LPCWSTR wzExtractDir, + __in_opt CAB_CALLBACK_PROGRESS pfnProgress, + __in_opt LPVOID pvContext, + __in DWORD64 dw64EmbeddedOffset + ) +{ + return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset); +} + +// +// private +// +/******************************************************************** + FDINotify -- wrapper that converts call convention from __cdecl to __stdcall. + + NOTE: Since netfx 1.1 supports only function pointers (delegates) + with __stdcall calling convention and cabinet api uses + __cdecl calling convention, we need this wrapper function. + netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate. + TODO: remove this when upgrading to netfx 2.0. +********************************************************************/ +static __callback INT_PTR DIAMONDAPI FDINotify( + __in FDINOTIFICATIONTYPE iNotification, + __inout FDINOTIFICATION *pFDINotify + ) +{ + if (NULL != v_pfnNetFx11Notify) + { + return v_pfnNetFx11Notify(iNotification, pFDINotify); + } + else + { + return (INT_PTR)0; + } +} + + +/******************************************************************** + CabOperation - helper function that enumerates or extracts files + from cabinet + + NOTE: wzCabinet must be full path to cabinet file + wzExtractFile can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa + pfnNotify is callback function to get notified for each file + in the cabinet. If it's NULL, files will be extracted. +********************************************************************/ +static 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 + ) +{ + HRESULT hr = S_OK; + BOOL fResult; + + LPWSTR sczCabinet = NULL; + LPWSTR pwz = NULL; + CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings + CHAR szCabFile[MAX_PATH * 4]; + + CAB_CALLBACK_STRUCT ccs; + PFNFDINOTIFY pfnFdiNotify; + + // + // ensure the cabinet.dll is loaded + // + if (!vhfdi) + { + hr = LoadCabinetDll(); + ExitOnFailure(hr, "failed to load CABINET.DLL"); + } + + hr = StrAllocString(&sczCabinet, wzCabinet, 0); + ExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet); + + // + // split the cabinet full path into directory and filename and convert to multi-byte (ick!) + // + pwz = FileFromPath(sczCabinet); + ExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet); + + if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL)) + { + ExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz); + } + + *pwz = '\0'; + + // If a full path was not provided, use the relative current directory. + if (wzCabinet == pwz) + { + hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); + ExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); + } + else + { + if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) + { + ExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); + } + } + + // + // iterate through files in cabinet extracting them to the callback function + // + ccs.fStopExtracting = FALSE; + ccs.pwzExtract = wzExtractFile; + ccs.pwzExtractDir = wzExtractDir; + ccs.pfnProgress = pfnProgress; + ccs.pvContext = pvContext; + + vdw64EmbeddedOffset = dw64EmbeddedOffset; + + // if pfnNotify is given, use it, otherwise use default callback + if (NULL == pfnNotify) + { + pfnFdiNotify = CabExtractCallback; + } + else + { + v_pfnNetFx11Notify = pfnNotify; + pfnFdiNotify = FDINotify; + } + fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); + if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure + { + ExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); + } + +LExit: + ReleaseStr(sczCabinet); + v_pfnNetFx11Notify = NULL; + + return hr; +} + +/**************************************************************************** + default extract routines + +****************************************************************************/ +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize) +{ + return MemAlloc(dwSize, FALSE); +} + + +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData) +{ + MemFree(pvData); +} + + +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode) +{ + HRESULT hr = S_OK; + INT_PTR pFile = -1; + LPWSTR sczCabFile = NULL; + + // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); + } + + hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); + + pFile = reinterpret_cast(::CreateFileW(sczCabFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); + if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) + { + ExitWithLastError(hr, "failed to open file: %ls", sczCabFile); + } + + if (vdw64EmbeddedOffset) + { + hr = CabExtractSeek(pFile, 0, 0); + ExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); + } + +LExit: + ReleaseStr(sczCabFile); + + return FAILED(hr) ? -1 : pFile; +} + + +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb) +{ + HRESULT hr = S_OK; + DWORD cbRead = 0; + + ExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read"); + if (!::ReadFile(reinterpret_cast(hf), pv, cb, &cbRead, NULL)) + { + ExitWithLastError(hr, "failed to read during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb) +{ + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + ExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write"); + if (!::WriteFile(reinterpret_cast(hf), pv, cb, &cbWrite, NULL)) + { + ExitWithLastError(hr, "failed to write during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype) +{ + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + dist += static_cast(vdw64EmbeddedOffset); + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + ExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + } + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); + if (0xFFFFFFFF == lMove) + { + ExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + } + +LExit: + return FAILED(hr) ? -1 : lMove - static_cast(vdw64EmbeddedOffset); +} + + +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf) +{ + HRESULT hr = S_OK; + + if (!::CloseHandle(reinterpret_cast(hf))) + { + ExitWithLastError(hr, "failed to close file during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : 0; +} + + +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify) +{ + Assert(pFDINotify->pv); + + HRESULT hr = S_OK; + INT_PTR ipResult = 0; // result to return on success + + CAB_CALLBACK_STRUCT* pccs = static_cast(pFDINotify->pv); + LPCSTR sz; + WCHAR wz[MAX_PATH]; + FILETIME ft; + + switch (iNotification) + { + case fdintCOPY_FILE: // begin extracting a resource from cabinet + ExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + + if (pccs->fStopExtracting) + { + ExitFunction1(hr = S_FALSE); // no more extracting + } + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (pccs->pfnProgress) + { + hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); + if (S_OK != hr) + { + ExitFunction(); + } + } + + if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) + { + // get the created date for the resource in the cabinet + FILETIME ftLocal; + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) + { + ExitWithLastError(hr, "failed to get time for resource: %ls", wz); + } + ::LocalFileTimeToFileTime(&ftLocal, &ft); + + + WCHAR wzPath[MAX_PATH]; + hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); + ExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); + hr = ::StringCchCatW(wzPath, countof(wzPath), wz); + ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + + ipResult = reinterpret_cast(::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); + if (INVALID_HANDLE_VALUE == reinterpret_cast(ipResult)) + { + ExitWithLastError(hr, "failed to create file: %s", wzPath); + } + + ::SetFileTime(reinterpret_cast(ipResult), &ft, &ft, &ft); // try to set the file time (who cares if it fails) + + if (::SetFilePointer(reinterpret_cast(ipResult), pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) + { + if (::SetEndOfFile(reinterpret_cast(ipResult))) + { + ::SetFilePointer(reinterpret_cast(ipResult), 0, NULL, FILE_BEGIN); // reset the file pointer + } + } + } + else // resource wasn't requested, skip it + { + hr = S_OK; + ipResult = 0; + } + + break; + case fdintCLOSE_FILE_INFO: // resource extraction complete + Assert(pFDINotify->hf && pFDINotify->psz1); + ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + ExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); + + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (NULL != pFDINotify->hf) // just close the file + { + ::CloseHandle(reinterpret_cast(pFDINotify->hf)); + } + + if (pccs->pfnProgress) + { + hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); + } + + if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going + { + ipResult = TRUE; + } + else // something went wrong or we only needed to extract one file + { + hr = S_OK; + ipResult = FALSE; + pccs->fStopExtracting = TRUE; + } + + break; + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + default: + AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); + }; + +LExit: + return (S_OK == hr) ? ipResult : -1; +} diff --git a/src/dutil/certutil.cpp b/src/dutil/certutil.cpp new file mode 100644 index 00000000..9c0ee256 --- /dev/null +++ b/src/dutil/certutil.cpp @@ -0,0 +1,327 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** +CertReadProperty - reads a property from the certificate. + +NOTE: call MemFree() on the returned pvValue. +********************************************************************/ +extern "C" HRESULT DAPI CertReadProperty( + __in PCCERT_CONTEXT pCertContext, + __in DWORD dwProperty, + __deref_out_bound LPVOID* ppvValue, + __out_opt DWORD* pcbValue + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb)) + { + ExitWithLastError(hr, "Failed to get size of certificate property."); + } + + pv = MemAlloc(cb, TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property."); + + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb)) + { + ExitWithLastError(hr, "Failed to get certificate property."); + } + + *ppvValue = pv; + pv = NULL; + + if (pcbValue) + { + *pcbValue = cb; + } + +LExit: + ReleaseMem(pv); + return hr; +} + + +extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp( + __in CMSG_SIGNER_INFO* pSignerInfo, + __out FILETIME* pftSigningTimestamp + ) +{ + HRESULT hr = S_OK; + CRYPT_INTEGER_BLOB* pBlob = NULL; + PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; + DWORD cbSigningTimestamp = sizeof(FILETIME); + + // Find the countersigner blob. The countersigner in Authenticode contains the time + // that signing took place. It's a "countersigner" because the signing time was sent + // off to the certificate authority in the sky to return the verified time signed. + for (DWORD i = 0; i < pSignerInfo->UnauthAttrs.cAttr; ++i) + { + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_counterSign, -1, pSignerInfo->UnauthAttrs.rgAttr[i].pszObjId, -1)) + { + pBlob = pSignerInfo->UnauthAttrs.rgAttr[i].rgValue; + break; + } + } + + if (!pBlob) + { + hr = TRUST_E_FAIL; + ExitOnFailure(hr, "Failed to find countersigner in signer information."); + } + + hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast(&pCounterSignerInfo), NULL); + ExitOnFailure(hr, "Failed to decode countersigner information."); + + pBlob = NULL; // reset the blob before searching for the signing time. + + // Find the signing time blob in the countersigner. + for (DWORD i = 0; i < pCounterSignerInfo->AuthAttrs.cAttr; ++i) + { + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_signingTime, -1, pCounterSignerInfo->AuthAttrs.rgAttr[i].pszObjId, -1)) + { + pBlob = pCounterSignerInfo->AuthAttrs.rgAttr[i].rgValue; + break; + } + } + + if (!pBlob) + { + hr = TRUST_E_FAIL; + ExitOnFailure(hr, "Failed to find signing time in countersigner information."); + } + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp)) + { + ExitWithLastError(hr, "Failed to decode countersigner signing timestamp."); + } + +LExit: + ReleaseMem(pCounterSignerInfo); + + return hr; +} + + +extern "C" HRESULT DAPI GetCryptProvFromCert( + __in_opt HWND hwnd, + __in PCCERT_CONTEXT pCert, + __out HCRYPTPROV *phCryptProv, + __out DWORD *pdwKeySpec, + __in BOOL *pfDidCryptAcquire, + __deref_opt_out LPWSTR *ppwszTmpContainer, + __deref_opt_out LPWSTR *ppwszProviderName, + __out DWORD *pdwProviderType + ) +{ + HRESULT hr = S_OK; + HMODULE hMsSign32 = NULL; + + typedef BOOL (WINAPI *GETCRYPTPROVFROMCERTPTR)(HWND, PCCERT_CONTEXT, HCRYPTPROV*, DWORD*,BOOL*,LPWSTR*,LPWSTR*,DWORD*); + GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL; + + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); + ExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + + pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert"); + ExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + + if (!pGetCryptProvFromCert(hwnd, + pCert, + phCryptProv, + pdwKeySpec, + pfDidCryptAcquire, + ppwszTmpContainer, + ppwszProviderName, + pdwProviderType)) + { + ExitWithLastError(hr, "Failed to get CSP from cert."); + } +LExit: + return hr; +} + +extern "C" HRESULT DAPI FreeCryptProvFromCert( + __in BOOL fAcquired, + __in HCRYPTPROV hProv, + __in_opt LPWSTR pwszCapiProvider, + __in DWORD dwProviderType, + __in_opt LPWSTR pwszTmpContainer + ) +{ + HRESULT hr = S_OK; + HMODULE hMsSign32 = NULL; + + typedef void (WINAPI *FREECRYPTPROVFROMCERT)(BOOL, HCRYPTPROV, LPWSTR, DWORD, LPWSTR); + FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL; + + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); + ExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + + pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert"); + ExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + + pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer); +LExit: + return hr; +} + +extern "C" HRESULT DAPI GetProvSecurityDesc( + __in HCRYPTPROV hProv, + __deref_out SECURITY_DESCRIPTOR** ppSecurity) +{ + HRESULT hr = S_OK; + ULONG ulSize = 0; + SECURITY_DESCRIPTOR* pSecurity = NULL; + + // Get the size of the security descriptor. + if (!::CryptGetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + NULL, + &ulSize, + DACL_SECURITY_INFORMATION)) + { + ExitWithLastError(hr, "Error getting security descriptor size for CSP."); + } + + // Allocate the memory for the security descriptor. + pSecurity = static_cast(MemAlloc(ulSize, TRUE)); + ExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL"); + + // Get the security descriptor. + if (!::CryptGetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + (BYTE*)pSecurity, + &ulSize, + DACL_SECURITY_INFORMATION)) + { + MemFree(pSecurity); + ExitWithLastError(hr, "Error getting security descriptor for CSP."); + } + *ppSecurity = pSecurity; + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI SetProvSecurityDesc( + __in HCRYPTPROV hProv, + __in SECURITY_DESCRIPTOR* pSecurity) +{ + HRESULT hr = S_OK; + + // Set the new security descriptor. + if (!::CryptSetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + (BYTE*)pSecurity, + DACL_SECURITY_INFORMATION)) + { + ExitWithLastError(hr, "Error setting security descriptor for CSP."); + } +LExit: + return hr; +} + +extern "C" BOOL DAPI CertHasPrivateKey( + __in PCCERT_CONTEXT pCertContext, + __out_opt DWORD* pdwKeySpec) +{ + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey = NULL; + DWORD dwKeySpec = 0; + // set CRYPT_ACQUIRE_CACHE_FLAG so that we don't have to release the private key handle + BOOL fResult = ::CryptAcquireCertificatePrivateKey( + pCertContext, + CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG, + 0, //pvReserved + &hPrivateKey, + &dwKeySpec, + NULL + ); + if (pdwKeySpec) + { + *pdwKeySpec = dwKeySpec; + } + return fResult; +} + + +extern "C" HRESULT DAPI CertInstallSingleCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + CERT_BLOB blob = { }; + + DWORD dwKeySpec = 0; + + HCRYPTPROV hCsp = NULL; + LPWSTR pwszTmpContainer = NULL; + LPWSTR pwszProviderName = NULL; + DWORD dwProviderType = 0; + BOOL fAcquired = TRUE; + + SECURITY_DESCRIPTOR* pSecurity = NULL; + SECURITY_DESCRIPTOR* pSecurityNew = NULL; + + // Update the friendly name of the certificate to be configured. + blob.pbData = (BYTE*)wzName; + blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null + + if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob)) + { + ExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName); + } + + if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) + { + ExitWithLastError(hr, "Failed to add certificate to the store."); + } + + // if the certificate has a private key, grant Administrators access + if (CertHasPrivateKey(pCertContext, &dwKeySpec)) + { + if (AT_KEYEXCHANGE == dwKeySpec || AT_SIGNATURE == dwKeySpec) + { + // We added a CSP key + hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType); + ExitOnFailure(hr, "Failed to get handle to CSP"); + + hr = GetProvSecurityDesc(hCsp, &pSecurity); + ExitOnFailure(hr, "Failed to get security descriptor of CSP"); + + hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew); + ExitOnFailure(hr, "Failed to create new security descriptor"); + + hr = SetProvSecurityDesc(hCsp, pSecurityNew); + ExitOnFailure(hr, "Failed to set Admin ACL on CSP"); + } + + if (CERT_NCRYPT_KEY_SPEC == dwKeySpec) + { + // We added a CNG key + // TODO change ACL on CNG key + } + } +LExit: + if (hCsp) + { + FreeCryptProvFromCert(fAcquired, hCsp, NULL, dwProviderType, NULL); + } + + ReleaseMem(pSecurity); + + if (pSecurityNew) + { + AclFreeSecurityDescriptor(pSecurityNew); + } + return hr; +} diff --git a/src/dutil/condutil.cpp b/src/dutil/condutil.cpp new file mode 100644 index 00000000..99923c18 --- /dev/null +++ b/src/dutil/condutil.cpp @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// function definitions + +/******************************************************************** +CondEvaluate - evaluates the condition using the given variables. +********************************************************************/ +extern "C" HRESULT DAPI CondEvaluate( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzCondition); + UNREFERENCED_PARAMETER(pf); + return E_NOTIMPL; +} diff --git a/src/dutil/conutil.cpp b/src/dutil/conutil.cpp new file mode 100644 index 00000000..4c820a1c --- /dev/null +++ b/src/dutil/conutil.cpp @@ -0,0 +1,656 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static HANDLE vhStdIn = INVALID_HANDLE_VALUE; +static HANDLE vhStdOut = INVALID_HANDLE_VALUE; +static BOOL vfConsoleIn = FALSE; +static BOOL vfConsoleOut = FALSE; +static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; + + +extern "C" HRESULT DAPI ConsoleInitialize() +{ + Assert(INVALID_HANDLE_VALUE == vhStdOut); + HRESULT hr = S_OK; + UINT er; + + vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdIn) + { + ExitOnLastError(hr, "failed to open stdin"); + } + + vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdOut) + { + ExitOnLastError(hr, "failed to open stdout"); + } + + // check if we have a std in on the console + if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo)) + { + vfConsoleIn = TRUE; + } + else + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleIn= FALSE; + hr = S_OK; + } + else + { + ExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); + } + } + + if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo)) + { + vfConsoleOut = TRUE; + } + else // no console + { + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleOut = FALSE; + hr = S_OK; + } + else + { + ExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); + } + } + +LExit: + if (FAILED(hr)) + { + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; + } + + return hr; +} + + +extern "C" void DAPI ConsoleUninitialize() +{ + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; +} + + +extern "C" void DAPI ConsoleGreen() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleRed() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleYellow() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleNormal() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes); + } +} + + +/******************************************************************** + ConsoleWrite - full color printfA without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWrite( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + { + ExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + } + + cbTotal += cbWrote; + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteLine - full color printfA plus newline without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWriteLine( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + LPCSTR szNewLine = "\r\n"; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + // + // write the string + // + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + ExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + + cbTotal += cbWrote; + } + + // + // write the newline + // + if (!::WriteFile(vhStdOut, reinterpret_cast(szNewLine), 2, &cbWrote, NULL)) + { + ExitOnLastError(hr, "failed to write newline to console"); + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteError - display an error to the screen + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d") +********************************************************************/ +HRESULT ConsoleWriteError( + HRESULT hrError, + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + LPSTR pszMessage = NULL; + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args); + va_end(args); + ExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); + + if (FAILED(hrError)) + { + hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage); + } + else + { + hr = ConsoleWriteLine(cc, "Error: %s", pszMessage); + } + +LExit: + ReleaseStr(pszMessage); + return hr; +} + + +/******************************************************************** + ConsoleReadW - get console input without libc + + NOTE: only supports reading ANSI characters +********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadW( + __deref_out_z LPWSTR* ppwzBuffer + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + Assert(ppwzBuffer); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + DWORD cch = 0; + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + cch = 64; + hr = StrAnsiAlloc(&psz, cch); + ExitOnFailure(hr, "failed to allocate memory to read from console"); + + // loop until we read the \r\n from the console + for (;;) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + ExitOnLastError(hr, "failed to read string from console"); + + cchTotalRead += cchRead; + if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1]) + { + psz[cchTotalRead - 2] = '\0'; // chop off the \r\n + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP); + +LExit: + ReleaseStr(psz); + return hr; +} + + +/******************************************************************** + ConsoleReadNonBlockingW - Read from the console without blocking + Won't work for redirected files (exe < txtfile), but will work for stdin redirected to + an anonymous or named pipe + + if (fReadLine), stop reading immediately when \r\n is found +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadNonBlockingW( + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, + __out DWORD* pcchSize, + BOOL fReadLine + ) +{ + Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize); + HRESULT hr = S_OK; + + LPSTR psz = NULL; + + ExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); + + DWORD dwRead; + DWORD dwNumInput; + + DWORD cchTotal = 0; + DWORD cch = 8; + + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + DWORD dwIndex = 0; + DWORD er; + + INPUT_RECORD ir; + WCHAR chIn; + + *ppwzBuffer = NULL; + *pcchSize = 0; + + // If we really have a handle to stdin, and not the end of a pipe + if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL)) + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE != er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + + if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead)) + { + ExitOnLastError(hr, "failed to peek at console input"); + } + + if (0 == dwRead) + { + ExitFunction1(hr = S_FALSE); + } + + for (/* dwRead from num of input events */; dwRead > 0; dwRead--) + { + if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput)) + { + ExitOnLastError(hr, "Failed to read input from console"); + } + + // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested + if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown) + { + chIn = ir.Event.KeyEvent.uChar.UnicodeChar; + + if (0 == cchTotal) + { + cchTotal = cch; + cch *= 2; + StrAlloc(ppwzBuffer, cch); + } + + (*ppwzBuffer)[dwIndex] = chIn; + + if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex])) + { + *ppwzBuffer[dwIndex - 1] = L'\0'; + dwIndex -= 1; + break; + } + + ++dwIndex; + cchTotal--; + } + } + + *pcchSize = dwIndex; + } + else + { + // otherwise, the peek worked, and we have the end of a pipe + if (0 == dwRead) + ExitFunction1(hr = S_FALSE); + + cch = 8; + hr = StrAnsiAlloc(&psz, cch); + ExitOnFailure(hr, "failed to allocate memory to read from console"); + + for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + { + ExitOnLastError(hr, "failed to read string from console"); + } + + cchTotalRead += cchRead; + if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead]) + { + psz[cchTotalRead - 1] = '\0'; // chop off the \r\n + cchTotalRead -= 1; + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + *pcchSize = cchTotalRead; + hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP); + } + +LExit: + ReleaseStr(psz); + + return hr; +} + + +/******************************************************************** + ConsoleReadStringA - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringA( + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (ppszCharBuffer && *ppszCharBuffer == NULL) + { + do + { + hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); + ExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleReadStringW - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringW( + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, + const DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (*ppwzCharBuffer == NULL) + { + do + { + hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); + ExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadHidden - set console input no echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadHidden(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + ::FlushConsoleInputBuffer(vhStdIn); + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) + { + ExitOnLastError(hr, "failed to set console input mode to be hidden"); + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadNormal - reset to echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadNormal(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) + { + ExitOnLastError(hr, "failed to set console input mode to be normal"); + } + +LExit: + return hr; +} + diff --git a/src/dutil/cryputil.cpp b/src/dutil/cryputil.cpp new file mode 100644 index 00000000..214704b4 --- /dev/null +++ b/src/dutil/cryputil.cpp @@ -0,0 +1,379 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL; +static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL; +static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL; +static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL; + +static HMODULE vhAdvApi32Dll = NULL; +static HMODULE vhCrypt32Dll = NULL; +static BOOL vfCrypInitialized = FALSE; + +// function definitions + +/******************************************************************** + CrypInitialize - initializes cryputil + +*********************************************************************/ +extern "C" HRESULT DAPI CrypInitialize( + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); + if (SUCCEEDED(hr)) + { + // Ignore failures - if these don't exist, we'll try the Crypt methods. + vpfnRtlEncryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction040")); + vpfnRtlDecryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction041")); + } + if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory) + { + hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll); + ExitOnFailure(hr, "Failed to load Crypt32.dll"); + + vpfnCryptProtectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory")); + if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory) + { + ExitWithLastError(hr, "Failed to load an encryption method"); + } + vpfnCryptUnprotectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory")); + if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory) + { + ExitWithLastError(hr, "Failed to load a decryption method"); + } + } + + vfCrypInitialized = TRUE; + +LExit: + return hr; +} + + +/******************************************************************** + CrypUninitialize - uninitializes cryputil + +*********************************************************************/ +extern "C" void DAPI CrypUninitialize( + ) +{ + if (vhAdvApi32Dll) + { + ::FreeLibrary(vhAdvApi32Dll); + vhAdvApi32Dll = NULL; + vpfnRtlEncryptMemory = NULL; + vpfnRtlDecryptMemory = NULL; + } + + if (vhCrypt32Dll) + { + ::FreeLibrary(vhCrypt32Dll); + vhCrypt32Dll = NULL; + vpfnCryptProtectMemory = NULL; + vpfnCryptUnprotectMemory = NULL; + } + + vfCrypInitialized = FALSE; +} + +extern "C" HRESULT DAPI CrypDecodeObject( + __in_z LPCSTR szStructType, + __in_ecount(cbData) const BYTE* pbData, + __in DWORD cbData, + __in DWORD dwFlags, + __out LPVOID* ppvObject, + __out_opt DWORD* pcbObject + ) +{ + HRESULT hr = S_OK; + LPVOID pvObject = NULL; + DWORD cbObject = 0; + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject)) + { + ExitWithLastError(hr, "Failed to decode object to determine size."); + } + + pvObject = MemAlloc(cbObject, TRUE); + ExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object."); + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject)) + { + ExitWithLastError(hr, "Failed to decode object."); + } + + *ppvObject = pvObject; + pvObject = NULL; + + if (pcbObject) + { + *pcbObject = cbObject; + } + +LExit: + ReleaseMem(pvObject); + + return hr; +} + + +extern "C" HRESULT DAPI CrypMsgGetParam( + __in HCRYPTMSG hCryptMsg, + __in DWORD dwType, + __in DWORD dwIndex, + __out LPVOID* ppvData, + __out_opt DWORD* pcbData + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb)) + { + ExitWithLastError(hr, "Failed to get crypt message parameter data size."); + } + + pv = MemAlloc(cb, TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter."); + + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb)) + { + ExitWithLastError(hr, "Failed to get crypt message parameter."); + } + + *ppvData = pv; + pv = NULL; + + if (pcbData) + { + *pcbData = cb; + } + +LExit: + ReleaseMem(pv); + + return hr; +} + + +extern "C" HRESULT DAPI CrypHashFile( + __in LPCWSTR wzFilePath, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // open input file + hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to open input file."); + } + + hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed); + ExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + + +extern "C" HRESULT DAPI CrypHashFileHandle( + __in HANDLE hFile, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ) +{ + HRESULT hr = S_OK; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + DWORD cbRead = 0; + BYTE rgbBuffer[4096] = { }; + const LARGE_INTEGER liZero = { }; + + // get handle to the crypto provider + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + ExitWithLastError(hr, "Failed to acquire crypto context."); + } + + // initiate hash + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) + { + ExitWithLastError(hr, "Failed to initiate hash."); + } + + for (;;) + { + // read data block + if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read data block."); + } + + if (!cbRead) + { + break; // end of file + } + + // hash data block + if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0)) + { + ExitWithLastError(hr, "Failed to hash data block."); + } + } + + // get hash value + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + ExitWithLastError(hr, "Failed to get hash value."); + } + + if (pqwBytesHashed) + { + if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT)) + { + ExitWithLastError(hr, "Failed to get file pointer."); + } + } + +LExit: + if (hHash) + { + ::CryptDestroyHash(hHash); + } + if (hProv) + { + ::CryptReleaseContext(hProv, 0); + } + + return hr; +} + +HRESULT DAPI CrypHashBuffer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash + ) +{ + HRESULT hr = S_OK; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + + // get handle to the crypto provider + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + ExitWithLastError(hr, "Failed to acquire crypto context."); + } + + // initiate hash + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) + { + ExitWithLastError(hr, "Failed to initiate hash."); + } + + if (!::CryptHashData(hHash, pbBuffer, static_cast(cbBuffer), 0)) + { + ExitWithLastError(hr, "Failed to hash data."); + } + + // get hash value + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + ExitWithLastError(hr, "Failed to get hash value."); + } + +LExit: + if (hHash) + { + ::CryptDestroyHash(hHash); + } + if (hProv) + { + ::CryptReleaseContext(hProv, 0); + } + + return hr; +} + +HRESULT DAPI CrypEncryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ) +{ + HRESULT hr = E_FAIL; + + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) + { + hr = E_INVALIDARG; + } + else if (vpfnRtlEncryptMemory) + { + hr = static_cast(vpfnRtlEncryptMemory(pData, cbData, dwFlags)); + } + else if (vpfnCryptProtectMemory) + { + if (vpfnCryptProtectMemory(pData, cbData, dwFlags)) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } + ExitOnFailure(hr, "Failed to encrypt memory"); +LExit: + return hr; +} + +HRESULT DAPI CrypDecryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ) +{ + HRESULT hr = E_FAIL; + + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) + { + hr = E_INVALIDARG; + } + else if (vpfnRtlDecryptMemory) + { + hr = static_cast(vpfnRtlDecryptMemory(pData, cbData, dwFlags)); + } + else if (vpfnCryptUnprotectMemory) + { + if (vpfnCryptUnprotectMemory(pData, cbData, dwFlags)) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } + ExitOnFailure(hr, "Failed to decrypt memory"); +LExit: + return hr; +} + diff --git a/src/dutil/dictutil.cpp b/src/dutil/dictutil.cpp new file mode 100644 index 00000000..1f0f9e43 --- /dev/null +++ b/src/dutil/dictutil.cpp @@ -0,0 +1,769 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// These should all be primes, and spaced reasonably apart (currently each is about 4x the last) +const DWORD MAX_BUCKET_SIZES[] = { + 503, + 2017, + 7937, + 32779, + 131111, + 524341, + 2097709, + 8390857, + 33563437, + 134253719, + 537014927, + 2148059509 + }; + +// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions +#define MAX_BUCKETS_TO_ITEMS_RATIO 8 + +enum DICT_TYPE +{ + DICT_INVALID = 0, + DICT_EMBEDDED_KEY = 1, + DICT_STRING_LIST = 2 +}; + +struct STRINGDICT_STRUCT +{ + DICT_TYPE dtType; + + // Optional flags to control the behavior of the dictionary. + DICT_FLAG dfFlags; + + // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated + DWORD dwBucketSizeIndex; + + // Number of items currently stored in the dict buckets + DWORD dwNumItems; + + // Byte offset of key within bucket value, for collision checking - see + // comments above DictCreateEmbeddedKey() implementation for further details + size_t cByteOffset; + + // The actual stored buckets + void **ppvBuckets; + + // The actual stored items in the order they were added (used for auto freeing or enumerating) + void **ppvItemList; + + // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm + void **ppvValueArray; +}; + +const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT); + +static HRESULT StringHash( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwNumBuckets, + __in_z LPCWSTR pszString, + __out LPDWORD pdwHash + ); +static BOOL IsMatchExact( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwMatchIndex, + __in_z LPCWSTR wzOriginalString + ); +static HRESULT GetValue( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out_opt void **ppvValue + ); +static HRESULT GetInsertIndex( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwBucketCount, + __in void **ppvBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ); +static HRESULT GetIndex( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ); +static LPCWSTR GetKey( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); +static HRESULT GrowDictionary( + __inout STRINGDICT_STRUCT *psd + ); +// These 2 helper functions allow us to safely handle dictutil consumers resizing +// the value array by storing "offsets" instead of raw void *'s in our buckets. +static void * TranslateOffsetToValue( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); +static void * TranslateValueToOffset( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); + +// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s). +// However, to support collision checking, the key needs to be represented in the "value" object (pointed to +// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer +// within the "value" object. Use the offsetof() macro to fill this out. +// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter, +// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide +// this parameter to dictutil if it is possible you will realloc the array. +// +// Use DictAddValue() and DictGetValue() with this dictionary type. +extern "C" HRESULT DAPI DictCreateWithEmbeddedKey( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in_opt void **ppvArray, + __in size_t cByteOffset, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + + ExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + + // Allocate the handle + *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); + ExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + + STRINGDICT_STRUCT *psd = static_cast(*psdHandle); + + // Fill out the new handle's values + psd->dtType = DICT_EMBEDDED_KEY; + psd->dfFlags = dfFlags; + psd->cByteOffset = cByteOffset; + psd->dwBucketSizeIndex = 0; + psd->dwNumItems = 0; + psd->ppvItemList = NULL; + psd->ppvValueArray = ppvArray; + + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime + // array based on expected number of items and items to buckets ratio + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end + // this loop past the end of the array! + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) + { + ++psd->dwBucketSizeIndex; + } + + // Finally, allocate our initial buckets + psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); + ExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + +LExit: + return hr; +} + +// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type. +extern "C" HRESULT DAPI DictCreateStringList( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + + ExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + + // Allocate the handle + *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); + ExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + + STRINGDICT_STRUCT *psd = static_cast(*psdHandle); + + // Fill out the new handle's values + psd->dtType = DICT_STRING_LIST; + psd->dfFlags = dfFlags; + psd->cByteOffset = 0; + psd->dwBucketSizeIndex = 0; + psd->dwNumItems = 0; + psd->ppvItemList = NULL; + psd->ppvValueArray = NULL; + + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime + // array based on expected number of items and items to buckets ratio + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end + // this loop past the end of the array! + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) + { + ++psd->dwBucketSizeIndex; + } + + // Finally, allocate our initial buckets + psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); + ExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictCreateStringListFromArray( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sd = NULL; + + hr = DictCreateStringList(&sd, cStringArray, dfFlags); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD i = 0; i < cStringArray; ++i) + { + const LPCWSTR wzKey = rgwzStringArray[i]; + + hr = DictKeyExists(sd, wzKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the string dictionary."); + } + else + { + hr = DictAddKey(sd, wzKey); + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey); + } + } + + *psdHandle = sd; + sd = NULL; + +LExit: + ReleaseDict(sd); + + return hr; +} + +extern "C" HRESULT DAPI DictCompareStringListToArray( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cStringArray; ++i) + { + hr = DictKeyExists(sdStringList, rgwzStringArray[i]); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the string dictionary."); + ExitFunction1(hr = S_OK); + } + } + + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH)); + +LExit: + return hr; +} + +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) +extern "C" HRESULT DAPI DictAddKey( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString + ) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + if (DICT_STRING_LIST != psd->dtType) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) + { + hr = GrowDictionary(psd); + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr) + { + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + hr = S_OK; + } + } + ExitOnFailure(hr, "Failed to grow dictionary"); + } + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex); + ExitOnFailure(hr, "Failed to get index to insert into"); + + hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); + ExitOnFailure(hr, "Failed to resize list of items in dictionary"); + ++psd->dwNumItems; + + hr = StrAllocString(reinterpret_cast(&(psd->ppvBuckets[dwIndex])), pszString, 0); + ExitOnFailure(hr, "Failed to allocate copy of string"); + + psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex]; + +LExit: + return hr; +} + +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) +extern "C" HRESULT DAPI DictAddValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in void *pvValue + ) +{ + HRESULT hr = S_OK; + void *pvOffset = NULL; + LPCWSTR wzKey = NULL; + DWORD dwIndex = 0; + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + ExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + if (DICT_EMBEDDED_KEY != psd->dtType) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + wzKey = GetKey(psd, pvValue); + ExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict"); + + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) + { + hr = GrowDictionary(psd); + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 ) + { + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + hr = S_OK; + } + } + ExitOnFailure(hr, "Failed to grow dictionary"); + } + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex); + ExitOnFailure(hr, "Failed to get index to insert into"); + + hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); + ExitOnFailure(hr, "Failed to resize list of items in dictionary"); + ++psd->dwNumItems; + + pvOffset = TranslateValueToOffset(psd, pvValue); + psd->ppvBuckets[dwIndex] = pvOffset; + psd->ppvItemList[psd->dwNumItems-1] = pvOffset; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictGetValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString, + __out void **ppvValue + ) +{ + HRESULT hr = S_OK; + + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + const STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + if (DICT_EMBEDDED_KEY != psd->dtType) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + hr = GetValue(psd, pszString, ppvValue); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to call internal GetValue()"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictKeyExists( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString + ) +{ + HRESULT hr = S_OK; + + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + const STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + // This works with either type of dictionary + hr = GetValue(psd, pszString, NULL); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to call internal GetValue()"); + +LExit: + return hr; +} + +extern "C" void DAPI DictDestroy( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle + ) +{ + DWORD i; + + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + if (DICT_STRING_LIST == psd->dtType) + { + for (i = 0; i < psd->dwNumItems; ++i) + { + ReleaseStr(reinterpret_cast(psd->ppvItemList[i])); + } + } + + ReleaseMem(psd->ppvItemList); + ReleaseMem(psd->ppvBuckets); + ReleaseMem(psd); +} + +static HRESULT StringHash( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwNumBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwHash + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzKey = NULL; + LPWSTR sczNewKey = NULL; + DWORD result = 0; + + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) + { + hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0); + ExitOnFailure(hr, "Failed to convert the string to upper-case."); + + wzKey = sczNewKey; + } + else + { + wzKey = pszString; + } + + while (*wzKey) + { + result = ~(*wzKey++ * 509) + result * 65599; + } + + *pdwHash = result % dwNumBuckets; + +LExit: + ReleaseStr(sczNewKey); + + return hr; +} + +static BOOL IsMatchExact( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwMatchIndex, + __in_z LPCWSTR wzOriginalString + ) +{ + LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex])); + DWORD dwFlags = 0; + + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) + { + dwFlags |= NORM_IGNORECASE; + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1)) + { + return TRUE; + } + + return FALSE; +} + +static HRESULT GetValue( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out_opt void **ppvValue + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + void *pvCandidateValue = NULL; + DWORD dwIndex = 0; + + ExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict"); + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); + ExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]); + + // If no match exists in the dict + if (NULL == pvCandidateValue) + { + if (NULL != ppvValue) + { + *ppvValue = NULL; + } + ExitFunction1(hr = E_NOTFOUND); + } + + hr = GetIndex(psd, pszString, &dwIndex); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to find index to get"); + + if (NULL != ppvValue) + { + *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]); + } + +LExit: + if (FAILED(hr) && NULL != ppvValue) + { + *ppvValue = NULL; + } + + return hr; +} + +static HRESULT GetInsertIndex( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwBucketCount, + __in void **ppvBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + + hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate); + ExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket +#pragma prefast(push) +#pragma prefast(disable:26007) + while (NULL != ppvBuckets[dwIndexCandidate]) +#pragma prefast(pop) + { + ++dwIndexCandidate; + + // If we got to the end of the array, wrap around to zero index + if (dwIndexCandidate >= dwBucketCount) + { + dwIndexCandidate = 0; + } + + // If we wrapped all the way back around to our original index, the dict is full - throw an error + if (dwIndexCandidate == dwOriginalIndexCandidate) + { + // The dict table is full - this error seems to be a reasonably close match + hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL); + ExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString); + } + } + + *pdwOutput = dwIndexCandidate; + +LExit: + return hr; +} + +static HRESULT GetIndex( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); + ExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + while (!IsMatchExact(psd, dwIndexCandidate, pszString)) + { + ++dwIndexCandidate; + + // If we got to the end of the array, wrap around to zero index + if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + dwIndexCandidate = 0; + } + + // If no match exists in the dict + if (NULL == psd->ppvBuckets[dwIndexCandidate]) + { + ExitFunction1(hr = E_NOTFOUND); + } + + // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such + if (dwIndexCandidate == dwOriginalIndexCandidate) + { + ExitFunction1(hr = E_NOTFOUND); + } + } + + *pdwOutput = dwIndexCandidate; + +LExit: + return hr; +} + +static LPCWSTR GetKey( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + const BYTE *lpByte = reinterpret_cast(pvValue); + + if (DICT_EMBEDDED_KEY == psd->dtType) + { + void *pvKey = reinterpret_cast(reinterpret_cast(pvValue) + psd->cByteOffset); + +#pragma prefast(push) +#pragma prefast(disable:26010) + return *(reinterpret_cast(pvKey)); +#pragma prefast(pop) + } + else + { + return (reinterpret_cast(lpByte)); + } +} + +static HRESULT GrowDictionary( + __inout STRINGDICT_STRUCT *psd + ) +{ + HRESULT hr = S_OK; + DWORD dwInsertIndex = 0; + LPCWSTR wzKey = NULL; + DWORD dwNewBucketSizeIndex = 0; + size_t cbAllocSize = 0; + void **ppvNewBuckets = NULL; + + dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1; + + if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL)); + } + + hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary"); + + ppvNewBuckets = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]); + + for (DWORD i = 0; i < psd->dwNumItems; ++i) + { + wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i])); + ExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value"); + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex); + ExitOnFailure(hr, "Failed to get index to insert into"); + + ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i]; + } + + psd->dwBucketSizeIndex = dwNewBucketSizeIndex; + ReleaseMem(psd->ppvBuckets); + psd->ppvBuckets = ppvNewBuckets; + ppvNewBuckets = NULL; + +LExit: + ReleaseMem(ppvNewBuckets); + + return hr; +} + +static void * TranslateOffsetToValue( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + if (NULL == pvValue) + { + return NULL; + } + + // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value + if (NULL != psd->ppvValueArray) + { + return reinterpret_cast(reinterpret_cast(pvValue) + reinterpret_cast(*psd->ppvValueArray) - 1); + } + else + { + return pvValue; + } +} + +static void * TranslateValueToOffset( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + if (NULL != psd->ppvValueArray) + { + // 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 + return reinterpret_cast(reinterpret_cast(pvValue) - reinterpret_cast(*psd->ppvValueArray) + 1); + } + else + { + return pvValue; + } +} diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp new file mode 100644 index 00000000..ddd621ac --- /dev/null +++ b/src/dutil/dirutil.cpp @@ -0,0 +1,395 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +/******************************************************************* + DirExists + +*******************************************************************/ +extern "C" BOOL DAPI DirExists( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + Assert(wzPath); + + BOOL fExists = FALSE; + + DWORD dwAttributes = ::GetFileAttributesW(wzPath); + if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here + { + ExitFunction(); + } + + if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (pdwAttributes) + { + *pdwAttributes = dwAttributes; + } + + fExists = TRUE; + } + +LExit: + return fExists; +} + + +/******************************************************************* + DirCreateTempPath + + *******************************************************************/ +extern "C" HRESULT DAPI DirCreateTempPath( + __in_z LPCWSTR wzPrefix, + __out_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD cchPath + ) +{ + Assert(wzPrefix); + Assert(wzPath); + + HRESULT hr = S_OK; + + WCHAR wzDir[MAX_PATH]; + WCHAR wzFile[MAX_PATH]; + DWORD cch = 0; + + cch = ::GetTempPathW(countof(wzDir), wzDir); + if (!cch || cch >= countof(wzDir)) + { + ExitWithLastError(hr, "Failed to GetTempPath."); + } + + if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) + { + ExitWithLastError(hr, "Failed to GetTempFileName."); + } + + hr = ::StringCchCopyW(wzPath, cchPath, wzFile); + +LExit: + return hr; +} + + +/******************************************************************* + DirEnsureExists + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureExists( + __in_z LPCWSTR wzPath, + __in_opt LPSECURITY_ATTRIBUTES psa + ) +{ + HRESULT hr = S_OK; + UINT er; + + // try to create this directory + if (!::CreateDirectoryW(wzPath, psa)) + { + // if the directory already exists, bail + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + ExitFunction1(hr = S_OK); + } + 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. + { + ExitFunction1(hr = S_OK); + } + + // get the parent path and try to create it + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzPath); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + // if there is no parent directory fail + ExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); + + *pwzLastSlash = L'\0'; // null terminate the parent path + hr = DirEnsureExists(wzPath, psa); // recurse! + *pwzLastSlash = L'\\'; // put the slash back + ExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); + + // try to create the directory now that all parents are created + if (!::CreateDirectoryW(wzPath, psa)) + { + // if the directory already exists for some reason no error + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + } + else + { + hr = S_OK; + } + } + +LExit: + return hr; +} + + +/******************************************************************* + DirEnsureDelete - removes an entire directory structure + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureDelete( + __in_z LPCWSTR wzPath, + __in BOOL fDeleteFiles, + __in BOOL fRecurse + ) +{ + HRESULT hr = S_OK; + DWORD dwDeleteFlags = 0; + + dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0; + dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0; + + hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags); + return hr; +} + + +/******************************************************************* + DirEnsureDeleteEx - removes an entire directory structure + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureDeleteEx( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ) +{ + Assert(wzPath && *wzPath); + + HRESULT hr = S_OK; + DWORD er; + + DWORD dwAttrib; + HANDLE hFind = INVALID_HANDLE_VALUE; + LPWSTR sczDelete = NULL; + WIN32_FIND_DATAW wfd; + + BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); + BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); + BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); + WCHAR wzTempDirectory[MAX_PATH] = { }; + WCHAR wzTempPath[MAX_PATH] = { }; + + if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) + { + er = ::GetLastError(); + if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory. + { + er = ERROR_PATH_NOT_FOUND; + } + hr = HRESULT_FROM_WIN32(er); + ExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); + } + + if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) + { + if (dwAttrib & FILE_ATTRIBUTE_READONLY) + { + if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL)) + { + ExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); + } + } + + // If we're deleting files and/or child directories loop through the contents of the directory. + if (fDeleteFiles || fRecurse) + { + if (fScheduleDelete) + { + if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) + { + ExitWithLastError(hr, "Failed to get temp directory."); + } + } + + // Delete everything in this directory. + hr = PathConcat(wzPath, L"*.*", &sczDelete); + ExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); + + hFind = ::FindFirstFileW(sczDelete, &wfd); + if (INVALID_HANDLE_VALUE == hFind) + { + ExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); + } + + do + { + // Skip the dot directories. + if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2]))) + { + continue; + } + + // For extra safety and to silence OACR. + wfd.cFileName[MAX_PATH - 1] = L'\0'; + + hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); + ExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); + + if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + hr = PathBackslashTerminate(&sczDelete); + ExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); + + hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call + if (FAILED(hr)) + { + // if we failed to delete a subdirectory, keep trying to finish any remaining files + ExitTrace(hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); + hr = S_OK; + } + } + else if (fDeleteFiles) // this is a file, just delete it + { + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) + { + if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL)) + { + ExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); + } + } + + if (!::DeleteFileW(sczDelete)) + { + if (fScheduleDelete) + { + if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) + { + ExitWithLastError(hr, "Failed to get temp file to move to."); + } + + // Try to move the file to the temp directory then schedule for delete, + // otherwise just schedule for delete. + if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) + { + ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + } + else + { + ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + } + } + else + { + ExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); + } + } + } + } while (::FindNextFileW(hFind, &wfd)); + + er = ::GetLastError(); + if (ERROR_NO_MORE_FILES == er) + { + hr = S_OK; + } + else + { + ExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); + } + } + + if (!::RemoveDirectoryW(wzPath)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete) + { + if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) + { + hr = S_OK; + } + } + + ExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); + } + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); + } + + Assert(S_OK == hr); + +LExit: + ReleaseFileFindHandle(hFind); + ReleaseStr(sczDelete); + + return hr; +} + + +/******************************************************************* + DirGetCurrent - gets the current directory. + +*******************************************************************/ +extern "C" HRESULT DAPI DirGetCurrent( + __deref_out_z LPWSTR* psczCurrentDirectory + ) +{ + HRESULT hr = S_OK; + DWORD_PTR cch = 0; + + if (psczCurrentDirectory && *psczCurrentDirectory) + { + hr = StrMaxLength(*psczCurrentDirectory, &cch); + ExitOnFailure(hr, "Failed to determine size of current directory."); + } + + DWORD cchRequired = ::GetCurrentDirectoryW(static_cast(cch), 0 == cch ? NULL : *psczCurrentDirectory); + if (0 == cchRequired) + { + ExitWithLastError(hr, "Failed to get current directory."); + } + else if (cch < cchRequired) + { + hr = StrAlloc(psczCurrentDirectory, cchRequired); + ExitOnFailure(hr, "Failed to allocate string for current directory."); + + if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory)) + { + ExitWithLastError(hr, "Failed to get current directory using allocated string."); + } + } + +LExit: + return hr; +} + + +/******************************************************************* + DirSetCurrent - sets the current directory. + +*******************************************************************/ +extern "C" HRESULT DAPI DirSetCurrent( + __in_z LPCWSTR wzDirectory + ) +{ + HRESULT hr = S_OK; + + if (!::SetCurrentDirectoryW(wzDirectory)) + { + ExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); + } + +LExit: + return hr; +} diff --git a/src/dutil/dlutil.cpp b/src/dutil/dlutil.cpp new file mode 100644 index 00000000..81455df0 --- /dev/null +++ b/src/dutil/dlutil.cpp @@ -0,0 +1,783 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include +#include + +static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024; +static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL }; + +// internal function declarations + +static HRESULT InitializeResume( + __in LPCWSTR wzDestinationPath, + __out LPWSTR* psczResumePath, + __out HANDLE* phResumeFile, + __out DWORD64* pdw64ResumeOffset + ); +static HRESULT GetResourceMetadata( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out DWORD64* pdw64ResourceSize, + __out FILETIME* pftResourceCreated + ); +static HRESULT DownloadResource( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_z LPCWSTR wzDestinationPath, + __in DWORD64 dw64AuthoredResourceLength, + __in DWORD64 dw64ResourceLength, + __in DWORD64 dw64ResumeOffset, + __in HANDLE hResumeFile, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ); +static HRESULT AllocateRangeRequestHeader( + __in DWORD64 dw64ResumeOffset, + __in DWORD64 dw64ResourceLength, + __deref_out_z LPWSTR* psczHeader + ); +static HRESULT WriteToFile( + __in HINTERNET hUrl, + __in HANDLE hPayloadFile, + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD64 dw64ResourceLength, + __in LPBYTE pbData, + __in DWORD cbData, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback + ); +static HRESULT UpdateResumeOffset( + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD cbData + ); +static HRESULT MakeRequest( + __in HINTERNET hSession, + __inout_z LPWSTR* psczSourceUrl, + __in_z_opt LPCWSTR wzMethod, + __in_z_opt LPCWSTR wzHeaders, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out HINTERNET* phConnect, + __out HINTERNET* phUrl, + __out BOOL* pfRangeRequestsAccepted + ); +static HRESULT OpenRequest( + __in HINTERNET hConnect, + __in_z_opt LPCWSTR wzMethod, + __in INTERNET_SCHEME scheme, + __in_z LPCWSTR wzResource, + __in_z_opt LPCWSTR wzQueryString, + __in_z_opt LPCWSTR wzHeader, + __out HINTERNET* phUrl + ); +static HRESULT SendRequest( + __in HINTERNET hUrl, + __inout_z LPWSTR* psczUrl, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetry, + __out BOOL* pfRangesAccepted + ); +static HRESULT AuthenticationRequired( + __in HINTERNET hUrl, + __in long lHttpCode, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); +static HRESULT DownloadGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ); +static HRESULT DownloadSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ); +// function definitions + +extern "C" HRESULT DAPI DownloadUrl( + __in DOWNLOAD_SOURCE* pDownloadSource, + __in DWORD64 dw64AuthoredDownloadSize, + __in LPCWSTR wzDestinationPath, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ) +{ + HRESULT hr = S_OK; + LPWSTR sczUrl = NULL; + HINTERNET hSession = NULL; + DWORD dwTimeout = 0; + LPWSTR sczResumePath = NULL; + HANDLE hResumeFile = INVALID_HANDLE_VALUE; + DWORD64 dw64ResumeOffset = 0; + DWORD64 dw64Size = 0; + FILETIME ftCreated = { }; + + // Copy the download source into a working variable to handle redirects then + // open the internet session. + hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); + ExitOnFailure(hr, "Failed to copy download source URL."); + + hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + ExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); + + // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. + PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); + if (0 < dwTimeout) + { + dwTimeout *= 1000; // convert to milliseconds. + ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + } + + // Get the resource size and creation time from the internet. + hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated); + ExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl); + + // Ignore failure to initialize resume because we will fall back to full download then + // download. + InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); + + hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate); + ExitOnFailure(hr, "Failed to download URL: %ls", sczUrl); + + // Cleanup the resume file because we successfully downloaded the whole file. + if (sczResumePath && *sczResumePath) + { + ::DeleteFileW(sczResumePath); + } + +LExit: + ReleaseFileHandle(hResumeFile); + ReleaseStr(sczResumePath); + ReleaseInternet(hSession); + ReleaseStr(sczUrl); + + return hr; +} + + +// internal helper functions + +static HRESULT InitializeResume( + __in LPCWSTR wzDestinationPath, + __out LPWSTR* psczResumePath, + __out HANDLE* phResumeFile, + __out DWORD64* pdw64ResumeOffset + ) +{ + HRESULT hr = S_OK; + HANDLE hResumeFile = INVALID_HANDLE_VALUE; + DWORD cbTotalReadResumeData = 0; + DWORD cbReadData = 0; + + *pdw64ResumeOffset = 0; + + hr = DownloadGetResumePath(wzDestinationPath, psczResumePath); + ExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); + + hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hResumeFile) + { + ExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath); + } + + do + { + if (!::ReadFile(hResumeFile, reinterpret_cast(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) + { + ExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath); + } + cbTotalReadResumeData += cbReadData; + } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); + + // Start over if we couldn't get a resume offset. + if (cbTotalReadResumeData != sizeof(DWORD64)) + { + *pdw64ResumeOffset = 0; + } + + *phResumeFile = hResumeFile; + hResumeFile = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hResumeFile); + return hr; +} + +static HRESULT GetResourceMetadata( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out DWORD64* pdw64ResourceSize, + __out FILETIME* pftResourceCreated + ) +{ + HRESULT hr = S_OK; + BOOL fRangeRequestsAccepted = TRUE; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + LONGLONG llLength = 0; + + hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); + ExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl); + + hr = InternetGetSizeByHandle(hUrl, &llLength); + if (FAILED(hr)) + { + llLength = 0; + hr = S_OK; + } + + *pdw64ResourceSize = llLength; + + // Get the last modified time from the server, we'll use that as our downloaded time here. If + // the server time isn't available then use the local system time. + hr = InternetGetCreateTimeByHandle(hUrl, pftResourceCreated); + if (FAILED(hr)) + { + ::GetSystemTimeAsFileTime(pftResourceCreated); + hr = S_OK; + } + +LExit: + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + return hr; +} + +static HRESULT DownloadResource( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_z LPCWSTR wzDestinationPath, + __in DWORD64 dw64AuthoredResourceLength, + __in DWORD64 dw64ResourceLength, + __in DWORD64 dw64ResumeOffset, + __in HANDLE hResumeFile, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ) +{ + HRESULT hr = S_OK; + HANDLE hPayloadFile = INVALID_HANDLE_VALUE; + DWORD cbMaxData = 64 * 1024; // 64 KB + BYTE* pbData = NULL; + BOOL fRangeRequestsAccepted = TRUE; + LPWSTR sczRangeRequestHeader = NULL; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + LONGLONG llLength = 0; + + hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hPayloadFile) + { + ExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath); + } + + // Allocate a memory block on a page boundary in case we want to do optimal writing. + pbData = static_cast(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); + ExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); + + // Let's try downloading the file assuming that range requests are accepted. If range requests + // are not supported we'll have to start over and accept the fact that we only get one shot + // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't + // like files that big. + while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) + { + hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); + ExitOnFailure(hr, "Failed to allocate range request header."); + + ReleaseNullInternet(hConnect); + ReleaseNullInternet(hUrl); + + hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); + ExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl); + + // If we didn't get the size of the resource from the initial "HEAD" request + // then let's try to get the size from this "GET" request. + if (0 == dw64ResourceLength) + { + hr = InternetGetSizeByHandle(hUrl, &llLength); + if (SUCCEEDED(hr)) + { + dw64ResourceLength = llLength; + } + else // server didn't tell us the resource length. + { + // Fallback to the authored size of the resource. However, since we + // don't really know the size on the server, don't try to use + // range requests either. + dw64ResourceLength = dw64AuthoredResourceLength; + fRangeRequestsAccepted = FALSE; + } + } + + // If we just tried to do a range request and found out that it isn't supported, start over. + if (!fRangeRequestsAccepted) + { + // TODO: log a message that the server did not accept range requests. + dw64ResumeOffset = 0; + } + + hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache); + ExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); + } + +LExit: + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + ReleaseStr(sczRangeRequestHeader); + if (pbData) + { + ::VirtualFree(pbData, 0, MEM_RELEASE); + } + ReleaseFileHandle(hPayloadFile); + + return hr; +} + +static HRESULT AllocateRangeRequestHeader( + __in DWORD64 dw64ResumeOffset, + __in DWORD64 dw64ResourceLength, + __deref_out_z LPWSTR* psczHeader + ) +{ + HRESULT hr = S_OK; + + // If the remaining length is less that 2GB we'll be able to ask for everything. + DWORD64 dw64RemainingLength = dw64ResourceLength - dw64ResumeOffset; + if (DOWNLOAD_ENGINE_TWO_GIGABYTES > dw64RemainingLength) + { + // If we have a resume offset, let's download everything from there. Otherwise, we'll + // just get everything with no headers in the way. + if (0 < dw64ResumeOffset) + { + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset); + ExitOnFailure(hr, "Failed to add range read header."); + } + else + { + ReleaseNullStr(*psczHeader); + } + } + else // we'll have to download in chunks. + { + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1); + ExitOnFailure(hr, "Failed to add range read header."); + } + +LExit: + return hr; +} + +static HRESULT WriteToFile( + __in HINTERNET hUrl, + __in HANDLE hPayloadFile, + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD64 dw64ResourceLength, + __in LPBYTE pbData, + __in DWORD cbData, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback + ) +{ + HRESULT hr = S_OK; + DWORD cbReadData = 0; + + hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN); + ExitOnFailure(hr, "Failed to seek to start point in file."); + + do + { + // Read bits from the internet. + if (!::InternetReadFile(hUrl, static_cast(pbData), cbData, &cbReadData)) + { + ExitWithLastError(hr, "Failed while reading from internet."); + } + + // Write bits to disk (if there are any). + if (cbReadData) + { + DWORD cbTotalWritten = 0; + DWORD cbWritten = 0; + do + { + if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL)) + { + ExitWithLastError(hr, "Failed to write data from internet."); + } + + cbTotalWritten += cbWritten; + } while (cbWritten && cbTotalWritten < cbReadData); + + // Ignore failure from updating resume file as this doesn't mean the download cannot succeed. + UpdateResumeOffset(pdw64ResumeOffset, hResumeFile, cbTotalWritten); + + if (pCallback && pCallback->pfnProgress) + { + hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile); + ExitOnFailure(hr, "UX aborted on cache progress."); + } + } + } while (cbReadData); + +LExit: + return hr; +} + +static HRESULT UpdateResumeOffset( + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + + *pdw64ResumeOffset += cbData; + + if (INVALID_HANDLE_VALUE != hResumeFile) + { + DWORD cbTotalWrittenResumeData = 0; + DWORD cbWrittenResumeData = 0; + + hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN); + ExitOnFailure(hr, "Failed to seek to start point in file."); + + do + { + // Ignore failure to write to the resume file as that should not prevent the download from happening. + if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL)) + { + ExitOnFailure(hr, "Failed to seek to write to file."); + } + + cbTotalWrittenResumeData += cbWrittenResumeData; + } while (cbWrittenResumeData && sizeof(DWORD64) > cbTotalWrittenResumeData); + } + +LExit: + return hr; +} + +static HRESULT MakeRequest( + __in HINTERNET hSession, + __inout_z LPWSTR* psczSourceUrl, + __in_z_opt LPCWSTR wzMethod, + __in_z_opt LPCWSTR wzHeaders, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out HINTERNET* phConnect, + __out HINTERNET* phUrl, + __out BOOL* pfRangeRequestsAccepted + ) +{ + HRESULT hr = S_OK; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + URI_INFO uri = { }; + + // Try to open the URL. + BOOL fRetry; + do + { + fRetry = FALSE; + + // If the URL was opened close it, so we can reopen it again. + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + + // Open the url. + hr = UriCrackEx(*psczSourceUrl, &uri); + ExitOnFailure(hr, "Failed to break URL into server and resource parts."); + + 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); + ExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); + + // Best effort set the proxy username and password, if they were provided. + if ((wzUser && *wzUser) && (wzPassword && *wzPassword)) + { + if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser))) + { + ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword)); + } + } + + hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl); + ExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl); + + hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted); + ExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl); + } while (fRetry); + + // Okay, we're all ready to start downloading. Update the connection information. + *phConnect = hConnect; + hConnect = NULL; + *phUrl = hUrl; + hUrl = NULL; + +LExit: + UriInfoUninitialize(&uri); + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + + return hr; +} + +static HRESULT OpenRequest( + __in HINTERNET hConnect, + __in_z_opt LPCWSTR wzMethod, + __in INTERNET_SCHEME scheme, + __in_z LPCWSTR wzResource, + __in_z_opt LPCWSTR wzQueryString, + __in_z_opt LPCWSTR wzHeader, + __out HINTERNET* phUrl + ) +{ + HRESULT hr = S_OK; + DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD; + LPWSTR sczResource = NULL; + HINTERNET hUrl = NULL; + + if (INTERNET_SCHEME_HTTPS == scheme) + { + dwRequestFlags |= INTERNET_FLAG_SECURE; + } + + // Allocate the resource name. + hr = StrAllocString(&sczResource, wzResource, 0); + ExitOnFailure(hr, "Failed to allocate string for resource URI."); + + if (wzQueryString && *wzQueryString) + { + hr = StrAllocConcat(&sczResource, wzQueryString, 0); + ExitOnFailure(hr, "Failed to append query strong to resource from URI."); + } + + // Open the request and add the header if provided. + hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL); + ExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); + + if (wzHeader && *wzHeader) + { + if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast(-1), HTTP_ADDREQ_FLAG_COALESCE)) + { + ExitWithLastError(hr, "Failed to add header to HTTP request."); + } + } + + *phUrl = hUrl; + hUrl = NULL; + +LExit: + ReleaseInternet(hUrl); + ReleaseStr(sczResource); + return hr; +} + +static HRESULT SendRequest( + __in HINTERNET hUrl, + __inout_z LPWSTR* psczUrl, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetry, + __out BOOL* pfRangesAccepted + ) +{ + HRESULT hr = S_OK; + BOOL fRetrySend = FALSE; + LONG lCode = 0; + + do + { + fRetrySend = FALSE; + + if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it. + LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl); + + // Try to get the HTTP status code and, if good, handle via the switch statement below but if it + // fails return the error code from the send request above as the result of the function. + HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); + ExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); + } + else // get the http status code. + { + hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); + ExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); + } + + switch (lCode) + { + case 200: // OK but range requests don't work. + *pfRangesAccepted = FALSE; + hr = S_OK; + break; + + case 206: // Partial content means that range requests work! + *pfRangesAccepted = TRUE; + hr = S_OK; + break; + + // redirection cases + case 301: __fallthrough; // file moved + case 302: __fallthrough; // temporary + case 303: // redirect method + hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl); + ExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl); + + *pfRetry = TRUE; + break; + + // error cases + case 400: // bad request + hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); + break; + + case 401: __fallthrough; // unauthorized + case 407: __fallthrough; // proxy unauthorized + hr = AuthenticationRequired(hUrl, lCode, pAuthenticate, &fRetrySend, pfRetry); + break; + + case 403: // forbidden + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + break; + + case 404: // file not found + case 410: // gone + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + break; + + case 405: // method not allowed + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + break; + + case 408: __fallthrough; // request timedout + case 504: // gateway timeout + hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); + break; + + case 414: // request URI too long + hr = CO_E_PATHTOOLONG; + break; + + case 502: __fallthrough; // server (through a gateway) was not found + case 503: // server unavailable + hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + break; + + case 418: // I'm a teapot. + default: + // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway) + // do not overwrite the error code from the failed request. Otherwise, the error was unexpected. + if (SUCCEEDED(hr)) + { + hr = E_UNEXPECTED; + } + + LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl); + break; + } + } while (fRetrySend); + +LExit: + return hr; +} + +static HRESULT AuthenticationRequired( + __in HINTERNET hUrl, + __in long lHttpCode, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ) +{ + Assert(401 == lHttpCode || 407 == lHttpCode); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + *pfRetrySend = FALSE; + *pfRetry = FALSE; + + if (pAuthenticate && pAuthenticate->pfnAuthenticate) + { + hr = (*pAuthenticate->pfnAuthenticate)(pAuthenticate->pv, hUrl, lHttpCode, pfRetrySend, pfRetry); + } + + return hr; +} + + +static HRESULT DownloadGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); + ExitOnFailure(hr, "Failed to create resume path."); + +LExit: + return hr; +} + +static HRESULT DownloadSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ) +{ + static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; + + HRESULT hr = S_OK; + DWORD dwResult = PROGRESS_CONTINUE; + LARGE_INTEGER liTotalSize = { }; + LARGE_INTEGER liTotalTransferred = { }; + + if (pCallback->pfnProgress) + { + liTotalSize.QuadPart = dw64Total; + liTotalTransferred.QuadPart = dw64Progress; + + dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); + switch (dwResult) + { + case PROGRESS_CONTINUE: + hr = S_OK; + break; + + case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? + case PROGRESS_STOP: + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + ExitOnRootFailure(hr, "UX aborted on download progress."); + + case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. + pCallback->pfnProgress = NULL; + hr = S_OK; + break; + + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid return code from progress routine."); + } + } + +LExit: + return hr; +} diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp new file mode 100644 index 00000000..3945d8c1 --- /dev/null +++ b/src/dutil/dutil.cpp @@ -0,0 +1,462 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// No need for OACR to warn us about using non-unicode APIs in this file. +#pragma prefast(disable:25068) + +// Asserts & Tracing + +const int DUTIL_STRING_BUFFER = 1024; +static HMODULE Dutil_hAssertModule = NULL; +static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL; +static BOOL Dutil_fNoAsserts = FALSE; +static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD; +static BOOL Dutil_fTraceFilenames = FALSE; + + +/******************************************************************* +Dutil_SetAssertModule + +*******************************************************************/ +extern "C" void DAPI Dutil_SetAssertModule( + __in HMODULE hAssertModule + ) +{ + Dutil_hAssertModule = hAssertModule; +} + + +/******************************************************************* +Dutil_SetAssertDisplayFunction + +*******************************************************************/ +extern "C" void DAPI Dutil_SetAssertDisplayFunction( + __in DUTIL_ASSERTDISPLAYFUNCTION pfn + ) +{ + Dutil_pfnDisplayAssert = pfn; +} + + +/******************************************************************* +Dutil_AssertMsg + +*******************************************************************/ +extern "C" void DAPI Dutil_AssertMsg( + __in_z LPCSTR szMessage + ) +{ + static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts) + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int id = IDRETRY; + HKEY hkDebug = NULL; + HANDLE hAssertFile = INVALID_HANDLE_VALUE; + char szPath[MAX_PATH] = { }; + DWORD cch = 0; + + if (fInAssert) + { + return; + } + fInAssert = TRUE; + + char szMsg[DUTIL_STRING_BUFFER]; + hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); + ExitOnFailure(hr, "failed to copy message while building assert message"); + + if (Dutil_pfnDisplayAssert) + { + // call custom function to display the assert string + if (!Dutil_pfnDisplayAssert(szMsg)) + { + ExitFunction(); + } + } + else + { + OutputDebugStringA(szMsg); + } + + if (!Dutil_fNoAsserts) + { + er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug); + if (ERROR_SUCCESS == er) + { + cch = countof(szPath); + er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast(szPath), &cch); + szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that. + if (ERROR_SUCCESS == er) + { + hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hAssertFile) + { + ::SetFilePointer(hAssertFile, 0, 0, FILE_END); + ::StringCchCatA(szMsg, countof(szMsg), "\r\n"); + ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); + } + } + } + + // if anything went wrong while fooling around with the registry, just show the usual assert dialog box + if (ERROR_SUCCESS != er) + { + hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); + ExitOnFailure(hr, "failed to concat string while building assert message"); + + id = ::MessageBoxA(0, szMsg, "Debug Assert Message", + MB_SERVICE_NOTIFICATION | MB_TOPMOST | + MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE); + } + } + + if (id == IDABORT) + { + if (Dutil_hAssertModule) + { + ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath)); + + 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()); + if (SUCCEEDED(hr)) + { + ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK); + } + } + + ::DebugBreak(); + } + else if (id == IDIGNORE) + { + Dutil_fNoAsserts = TRUE; + } + +LExit: + ReleaseFileHandle(hAssertFile); + ReleaseRegKey(hkDebug); + fInAssert = FALSE; +} + + +/******************************************************************* +Dutil_Assert + +*******************************************************************/ +extern "C" void DAPI Dutil_Assert( + __in_z LPCSTR szFile, + __in int iLine + ) +{ + HRESULT hr = S_OK; + char szMessage[DUTIL_STRING_BUFFER] = { }; + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine); + if (SUCCEEDED(hr)) + { + Dutil_AssertMsg(szMessage); + } + else + { + Dutil_AssertMsg("Assert failed to build string"); + } +} + + +/******************************************************************* +Dutil_AssertSz + +*******************************************************************/ +extern "C" void DAPI Dutil_AssertSz( + __in_z LPCSTR szFile, + __in int iLine, + __in_z __format_string LPCSTR szMsg + ) +{ + HRESULT hr = S_OK; + char szMessage[DUTIL_STRING_BUFFER] = { }; + + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg); + if (SUCCEEDED(hr)) + { + Dutil_AssertMsg(szMessage); + } + else + { + Dutil_AssertMsg("Assert failed to build string"); + } +} + + +/******************************************************************* +Dutil_TraceSetLevel + +*******************************************************************/ +extern "C" void DAPI Dutil_TraceSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fTraceFilenames + ) +{ + Dutil_rlCurrentTrace = rl; + Dutil_fTraceFilenames = fTraceFilenames; +} + + +/******************************************************************* +Dutil_TraceGetLevel + +*******************************************************************/ +extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel() +{ + return Dutil_rlCurrentTrace; +} + + +/******************************************************************* +Dutil_Trace + +*******************************************************************/ +extern "C" void DAPI Dutil_Trace( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level"); + + HRESULT hr = S_OK; + char szOutput[DUTIL_STRING_BUFFER] = { }; + char szMsg[DUTIL_STRING_BUFFER] = { }; + + if (Dutil_rlCurrentTrace < rl) + { + return; + } + + va_list args; + va_start(args, szFormat); + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); + va_end(args); + + if (SUCCEEDED(hr)) + { + LPCSTR szPrefix = "Trace/u"; + switch (rl) + { + case REPORT_STANDARD: + szPrefix = "Trace/s"; + break; + case REPORT_VERBOSE: + szPrefix = "Trace/v"; + break; + case REPORT_DEBUG: + szPrefix = "Trace/d"; + break; + } + + if (Dutil_fTraceFilenames) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput); + } + + if (SUCCEEDED(hr)) + { + OutputDebugStringA(szMsg); + } + // else fall through to the case below + } + + if (FAILED(hr)) + { + if (Dutil_fTraceFilenames) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n"); + } + + szMsg[countof(szMsg)-1] = '\0'; + OutputDebugStringA(szMsg); + } +} + + +/******************************************************************* +Dutil_TraceError + +*******************************************************************/ +extern "C" void DAPI Dutil_TraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + char szOutput[DUTIL_STRING_BUFFER] = { }; + char szMsg[DUTIL_STRING_BUFFER] = { }; + + // if this is NOT an error report and we're not logging at this level, bail + if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) + { + return; + } + + va_list args; + va_start(args, szFormat); + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); + va_end(args); + + if (SUCCEEDED(hr)) + { + if (Dutil_fTraceFilenames) + { + if (FAILED(hrError)) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput); + } + } + else + { + if (FAILED(hrError)) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput); + } + } + + if (SUCCEEDED(hr)) + { + OutputDebugStringA(szMsg); + } + // else fall through to the failure case below + } + + if (FAILED(hr)) + { + if (Dutil_fTraceFilenames) + { + if (FAILED(hrError)) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine); + } + } + else + { + if (FAILED(hrError)) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n"); + } + } + + szMsg[countof(szMsg)-1] = '\0'; + OutputDebugStringA(szMsg); + } +} + + + +/******************************************************************* +Dutil_RootFailure + +*******************************************************************/ +extern "C" void DAPI Dutil_RootFailure( + __in_z LPCSTR szFile, + __in int iLine, + __in HRESULT hrError + ) +{ +#ifndef DEBUG + UNREFERENCED_PARAMETER(szFile); + UNREFERENCED_PARAMETER(iLine); + UNREFERENCED_PARAMETER(hrError); +#endif // DEBUG + + TraceError(hrError, "Root failure at %s:%d", szFile, iLine); +} + +/******************************************************************* + LoadSystemLibrary - Fully qualifies the path to a module in the + Windows system directory and loads it. + + Returns + E_MODNOTFOUND - The module could not be found. + * - Another error occured. +********************************************************************/ +extern "C" HRESULT DAPI LoadSystemLibrary( + __in_z LPCWSTR wzModuleName, + __out HMODULE *phModule + ) +{ + HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL); + return hr; +} + +/******************************************************************* + LoadSystemLibraryWithPath - Fully qualifies the path to a module in + the Windows system directory and loads it + and returns the path + + Returns + E_MODNOTFOUND - The module could not be found. + * - Another error occured. +********************************************************************/ +extern "C" HRESULT DAPI LoadSystemLibraryWithPath( + __in_z LPCWSTR wzModuleName, + __out HMODULE *phModule, + __deref_out_z_opt LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + DWORD cch = 0; + WCHAR wzPath[MAX_PATH] = { }; + + cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); + ExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); + + if (L'\\' != wzPath[cch - 1]) + { + hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); + ExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); + } + + hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); + ExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); + + *phModule = ::LoadLibraryW(wzPath); + ExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); + + if (psczPath) + { + hr = StrAllocString(psczPath, wzPath, MAX_PATH); + ExitOnFailure(hr, "Failed to copy the path to library."); + } + +LExit: + return hr; +} diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj new file mode 100644 index 00000000..7bd78f1e --- /dev/null +++ b/src/dutil/dutil.vcxproj @@ -0,0 +1,300 @@ + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + Itanium + + + Release + Itanium + + + Debug + ARM + + + Release + ARM + + + + + {1244E671-F108-4334-BA52-8A7517F26ECD} + StaticLibrary + dutil + true + v141_xp + MultiByte + + + + + + true + + + false + true + + + true + + + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + precomp.h + + + Console + advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies) + + + + + Use + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + precomp.h + + + Console + advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + precomp.h + + + Console + true + true + advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + precomp.h + + + Console + true + true + advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + Create + + 4091 + + + + + + + + + + + + + + + + + 4996 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters new file mode 100644 index 00000000..644f45a8 --- /dev/null +++ b/src/dutil/dutil.vcxproj.filters @@ -0,0 +1,365 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + \ No newline at end of file diff --git a/src/dutil/eseutil.cpp b/src/dutil/eseutil.cpp new file mode 100644 index 00000000..1ff8e82c --- /dev/null +++ b/src/dutil/eseutil.cpp @@ -0,0 +1,1308 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +struct ESE_QUERY +{ + ESE_QUERY_TYPE qtQueryType; + BOOL fIndexRangeSet; + + JET_SESID jsSession; + JET_TABLEID jtTable; + + DWORD dwColumns; + void *pvData[10]; // The data queried for for this column + DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData +}; + +// Todo: convert more JET_ERR to HRESULTS here +HRESULT HresultFromJetError(JET_ERR jEr) +{ + HRESULT hr = S_OK; + + switch (jEr) + { + case JET_errSuccess: + return S_OK; + + case JET_wrnNyi: + return E_NOTIMPL; + break; + + case JET_errOutOfMemory: + hr = E_OUTOFMEMORY; + break; + + case JET_errInvalidParameter: __fallthrough; + case JET_errInvalidInstance: + hr = E_INVALIDARG; + break; + + case JET_errDatabaseInUse: + hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE); + break; + + case JET_errKeyDuplicate: + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + break; + + case JET_errInvalidSystemPath: __fallthrough; + case JET_errInvalidLogDirectory: __fallthrough; + case JET_errInvalidPath: __fallthrough; + case JET_errDatabaseInvalidPath: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); + break; + + case JET_errDatabaseLocked: + hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT); + break; + + case JET_errInvalidDatabase: + hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION); + break; + + case JET_errCallbackNotResolved: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + break; + + case JET_errNoCurrentRecord: __fallthrough; + case JET_errRecordNotFound: __fallthrough; + case JET_errFileNotFound: __fallthrough; + case JET_errObjectNotFound: + hr = E_NOTFOUND; + break; + + case JET_wrnBufferTruncated: + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + break; + + case JET_errFileAccessDenied: + hr = E_ACCESSDENIED; + break; + + default: + hr = E_FAIL; + } + + // 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 + ExitTrace(hr, "Encountered Jet Error: 0x%08x", jEr); + + return hr; +} + +#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTrace(x, s, __VA_ARGS__); goto LExit; }} +#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }} + +HRESULT DAPI EseBeginSession( + __out JET_INSTANCE *pjiInstance, + __out JET_SESID *pjsSession, + __in_z LPCWSTR pszInstance, + __in_z LPCWSTR pszPath + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiInstance = NULL; + LPSTR pszAnsiPath = NULL; + + hr = DirEnsureExists(pszPath, NULL); + ExitOnFailure(hr, "Failed to ensure database directory exists"); + + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, + // likely breaking everyone with unicode characters in their path. + hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting instance name to ansi"); + + hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting session path name to ansi"); + + jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance); + ExitOnJetFailure(jEr, hr, "Failed to create instance"); + + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath); + ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath); + + // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution) + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath); + ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath); + + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter"); + + // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too) + + jEr = JetInit(pjiInstance); + ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance"); + + jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL); + ExitOnJetFailure(jEr, hr, "Failed to begin jet session"); + +LExit: + ReleaseStr(pszAnsiInstance); + ReleaseStr(pszAnsiPath); + + return hr; +} + +HRESULT DAPI EseEndSession( + __in JET_INSTANCE jiInstance, + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetEndSession(jsSession, 0); + ExitOnJetFailure(jEr, hr, "Failed to end jet session"); + + jEr = JetTerm(jiInstance); + ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance"); + +LExit: + return hr; +} + +// Utility function used by EnsureSchema() +HRESULT AllocColumnCreateStruct( + __in const ESE_TABLE_SCHEMA *ptsSchema, + __deref_out JET_COLUMNCREATE **ppjccColumnCreate + ) +{ + HRESULT hr = S_OK; + DWORD_PTR i; + size_t cbAllocSize = 0; + + hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize)); + ExitOnFailure(hr, "Maximum allocation exceeded."); + + *ppjccColumnCreate = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database"); + + for (i = 0; i < ptsSchema->dwColumns; ++i) + { + (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE); + + hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); + ExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName); + + (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType; + + if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 256; + } + else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 2147483648; + (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged + ptsSchema->pcsColumns[i].fNullable = TRUE; + } + else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 4; + + if (ptsSchema->pcsColumns[i].fAutoIncrement) + { + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement; + } + } + + if (!(ptsSchema->pcsColumns[i].fNullable)) + { + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL; + } + + (*ppjccColumnCreate)[i].pvDefault = NULL; + (*ppjccColumnCreate)[i].cbDefault = 0; + (*ppjccColumnCreate)[i].cp = 1200; + (*ppjccColumnCreate)[i].columnid = 0; + (*ppjccColumnCreate)[i].err = 0; + } + +LExit: + return hr; +} + +HRESULT FreeColumnCreateStruct( + __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate, + __in DWORD dwColumns + ) +{ + HRESULT hr = S_OK; + DWORD i; + + for (i = 0; i < dwColumns; ++i) + { + ReleaseStr((pjccColumnCreate[i]).szColumnName); + } + + hr = MemFree(pjccColumnCreate); + ExitOnFailure(hr, "Failed to release core column create struct"); + +LExit: + return hr; +} + +// Utility function used by EnsureSchema() +HRESULT AllocIndexCreateStruct( + __in const ESE_TABLE_SCHEMA *ptsSchema, + __deref_out JET_INDEXCREATE **ppjicIndexCreate + ) +{ + HRESULT hr = S_OK; + LPSTR pszMultiSzKeys = NULL; + LPSTR pszIndexName = NULL; + LPSTR pszTempString = NULL; + BOOL fKeyColumns = FALSE; + DWORD_PTR i; + + for (i=0; i < ptsSchema->dwColumns; ++i) + { + if (ptsSchema->pcsColumns[i].fKey) + { + hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); + ExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName); + + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0); + ExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString); + + hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0); + ExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString); + + ReleaseNullStr(pszTempString); + + // All question marks will be converted to null characters later; this is just to trick dutil + // into letting us create an ansi, double-null-terminated list of single-null-terminated strings + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0); + ExitOnFailure(hr, "Failed to append placeholder character to multisz string: %ls", pszMultiSzKeys); + + // Record that at least one key column was found + fKeyColumns = TRUE; + } + } + + // If no key columns were found, don't create an index - just return + if (!fKeyColumns) + { + ExitFunction1(hr = S_OK); + } + + hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP); + ExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName); + + hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0); + ExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName); + + *ppjicIndexCreate = static_cast(MemAlloc(sizeof(JET_INDEXCREATE), TRUE)); + ExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); + + // Record the size including both null terminators - the struct requires this + DWORD dwSize = 0; + dwSize = lstrlen(pszMultiSzKeys) + 1; // add 1 to include null character at the end + ExitOnFailure(hr, "Failed to get size of keys string"); + + // At this point convert all question marks to null characters + for (i = 0; i < dwSize; ++i) + { + if ('?' == pszMultiSzKeys[i]) + { + pszMultiSzKeys[i] = '\0'; + } + } + + (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE); + (*ppjicIndexCreate)->szIndexName = pszIndexName; + (*ppjicIndexCreate)->szKey = pszMultiSzKeys; + (*ppjicIndexCreate)->cbKey = dwSize; + (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary; + (*ppjicIndexCreate)->ulDensity = 80; + (*ppjicIndexCreate)->lcid = 1033; + (*ppjicIndexCreate)->pidxunicode = NULL; + (*ppjicIndexCreate)->cbVarSegMac = 0; + (*ppjicIndexCreate)->rgconditionalcolumn = NULL; + (*ppjicIndexCreate)->cConditionalColumn = 0; + (*ppjicIndexCreate)->err = 0; + +LExit: + ReleaseStr(pszTempString); + + return hr; +} + +HRESULT EnsureSchema( + __in JET_DBID jdbDb, + __in JET_SESID jsSession, + __in ESE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BOOL fTransaction = FALSE; + DWORD dwTable; + DWORD dwColumn; + JET_TABLECREATE jtTableCreate = { }; + + // Set parameters which apply to all tables here + jtTableCreate.cbStruct = sizeof(jtTableCreate); + jtTableCreate.ulPages = 100; + jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value" + jtTableCreate.cIndexes = 1; + + hr = EseBeginTransaction(jsSession); + ExitOnFailure(hr, "Failed to begin transaction to create tables"); + fTransaction = TRUE; + + for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable) + { + // Don't free this pointer - it's just a shortcut to the current table's name within the struct + LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName; + + // Ensure table exists + hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable); + if (E_NOTFOUND == hr) // if the table is missing, create it + { + // Fill out the JET_TABLECREATE struct + hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting table name to ansi"); + + hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate); + ExitOnFailure(hr, "Failed to allocate column create struct"); + + hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate); + ExitOnFailure(hr, "Failed to allocate index create struct"); + + jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns; + jtTableCreate.tableid = NULL; + + // TODO: Investigate why we can't create a table without a key column? + // Actually create the table using our JET_TABLECREATE struct + jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate); + ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName); + + // Record the table ID in our cache + pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid; + + // Record the column IDs in our cache + for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) + { + pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid; + } + + // Free and NULL things we allocated in this struct + ReleaseNullStr(jtTableCreate.szTableName); + + hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); + ExitOnFailure(hr, "Failed to free column create struct"); + jtTableCreate.rgcolumncreate = NULL; + } + else + { + // If the table already exists, grab the column ids and put them into our cache + for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) + { + // Don't free this - it's just a shortcut to the current column within the struct + ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]); + ULONG ulColumnSize = 0; + BOOL fNullable = pcsColumn->fNullable; + + // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out! + if (JET_coltypText == pcsColumn->jcColumnType) + { + ulColumnSize = 256; + } + else if (JET_coltypLongText == pcsColumn->jcColumnType) + { + ulColumnSize = 2147483648; + fNullable = TRUE; + } + else if (JET_coltypLong == pcsColumn->jcColumnType) + { + ulColumnSize = 4; + fNullable = TRUE; + } + + hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn); + ExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName); + } + } + } + +LExit: + ReleaseStr(jtTableCreate.szTableName); + + if (NULL != jtTableCreate.rgcolumncreate) + { + // Don't record the HRESULT here or it will override the return value of this function + FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); + } + + if (fTransaction) + { + EseCommitTransaction(jsSession); + } + + return hr; +} + +// Todo: support overwrite flag? Unfortunately, requires WinXP and up +// 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 +HRESULT DAPI EseEnsureDatabase( + __in JET_SESID jsSession, + __in_z LPCWSTR pszFile, + __in ESE_DATABASE_SCHEMA *pdsSchema, + __out JET_DBID* pjdbDb, + __in BOOL fExclusive, + __in BOOL fReadonly + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jgrOptions = 0; + LPWSTR pszDir = NULL; + LPSTR pszAnsiFile = NULL; + + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, + // likely breaking all those with unicode characters in their path. + hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting database name to ansi"); + + hr = PathGetDirectory(pszFile, &pszDir); + ExitOnFailure(hr, "Failed to get directory that will contain database file"); + + hr = DirEnsureExists(pszDir, NULL); + ExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir); + + if (FileExistsEx(pszFile, NULL)) + { + if (fReadonly) + { + jgrOptions = jgrOptions | JET_bitDbReadOnly; + } + + jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile); + + // This flag doesn't apply to attach, only applies to Open, so only set it after the attach + if (fExclusive) + { + jgrOptions = jgrOptions | JET_bitDbExclusive; + } + + jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile); + } + else + { + jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0); + ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile); + } + + hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema); + ExitOnFailure(hr, "Failed to ensure database schema matches expectations"); + +LExit: + ReleaseStr(pszDir); + ReleaseStr(pszAnsiFile); + + return hr; +} + +HRESULT DAPI EseCloseDatabase( + __in JET_SESID jsSession, + __in JET_DBID jdbDb + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jgrOptions = 0; + + jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to close database"); + +LExit: + return hr; +} + +HRESULT DAPI EseCreateTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiTable = NULL; + + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting table name to ansi"); + + jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable); + ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable); + +LExit: + ReleaseStr(pszAnsiTable); + + return hr; +} + +HRESULT DAPI EseOpenTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiTable = NULL; + + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting table name to ansi"); + + jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable); + ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable); + +LExit: + ReleaseStr(pszAnsiTable); + + return hr; +} + +HRESULT DAPI EseCloseTable( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetCloseTable(jsSession, jtTable); + ExitOnJetFailure(jEr, hr, "Failed to close table"); + +LExit: + return hr; +} + +HRESULT DAPI EseEnsureColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __in JET_COLTYP jcColumnType, + __in ULONG ulColumnSize, + __in BOOL fFixed, + __in BOOL fNullable, + __out_opt JET_COLUMNID *pjcColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiColumnName = NULL; + JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) }; + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; + + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting column name to ansi"); + + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); + if (JET_errSuccess == jEr) + { + // Return the found columnID + if (NULL != pjcColumn) + { + *pjcColumn = jcdTempBase.columnid; + } + + ExitFunction1(hr = S_OK); + } + else if (JET_errColumnNotFound == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); + + jcdColumnDef.columnid = 0; + jcdColumnDef.coltyp = jcColumnType; + jcdColumnDef.wCountry = 0; + jcdColumnDef.langid = 0; + jcdColumnDef.cp = 1200; + jcdColumnDef.wCollate = 0; + jcdColumnDef.cbMax = ulColumnSize; + jcdColumnDef.grbit = 0; + + if (fFixed) + { + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed; + } + if (!fNullable) + { + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL; + } + + jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn); + ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName); + +LExit: + ReleaseStr(pszAnsiColumnName); + + return hr; +} + +HRESULT DAPI EseGetColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __out JET_COLUMNID *pjcColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiColumnName = NULL; + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; + + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); + ExitOnFailure(hr, "Failed converting column name to ansi"); + + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); + if (JET_errSuccess == jEr) + { + // Return the found columnID + if (NULL != pjcColumn) + { + *pjcColumn = jcdTempBase.columnid; + } + + ExitFunction1(hr = S_OK); + } + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); + +LExit: + ReleaseStr(pszAnsiColumnName); + + return hr; +} + +HRESULT DAPI EseMoveCursor( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in LONG lRow + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetMove(jsSession, jtTable, lRow, 0); + ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow); + +LExit: + return hr; +} + +HRESULT DAPI EseDeleteRow( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetDelete(jsSession, jtTable); + ExitOnJetFailure(jEr, hr, "Failed to delete row"); + +LExit: + return hr; +} + +HRESULT DAPI EseBeginTransaction( + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetBeginTransaction(jsSession); + ExitOnJetFailure(jEr, hr, "Failed to begin transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EseRollbackTransaction( + __in JET_SESID jsSession, + __in BOOL fAll + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0); + ExitOnJetFailure(jEr, hr, "Failed to rollback transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EseCommitTransaction( + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetCommitTransaction(jsSession, 0); + ExitOnJetFailure(jEr, hr, "Failed to commit transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EsePrepareUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ULONG ulPrep + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep); + ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep); + +LExit: + return hr; +} + +HRESULT DAPI EseFinishUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in BOOL fSeekToInsertedRecord + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + unsigned char rgbBookmark[JET_cbBookmarkMost + 1]; + DWORD cbBookmark; + + if (fSeekToInsertedRecord) + { + jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark); + ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark"); + + jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark); + ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark"); + } + else + { + jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)"); + } + +LExit: + // 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 + if (FAILED(hr)) + { + JetPrepareUpdate(jsSession, jtTable, JET_prepCancel); + } + + return hr; +} + +HRESULT DAPI EseSetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast(cbBuffer), 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in BOOL fValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BYTE bValue = fValue ? 0xFF : 0x00; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_z LPCWSTR pwzValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + ULONG cbValueSize = static_cast((wcslen(pwzValue) + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnEmpty( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + ULONG ulActualSize = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); + if (JET_wrnBufferTruncated == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record"); + + if (NULL == *ppbBuffer) + { + *ppbBuffer = reinterpret_cast(MemAlloc(ulActualSize, FALSE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column"); + } + else + { + *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, ulActualSize, FALSE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column"); + } + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record"); + + *piBuffer = static_cast(ulActualSize); + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(*ppbBuffer); + } + + return hr; +} + +HRESULT DAPI EseGetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out DWORD *pdwValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record"); + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out BOOL *pfValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BYTE bValue = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record"); + + if (bValue == 0) + { + *pfValue = FALSE; + } + else + { + *pfValue = TRUE; + } + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out LPWSTR *ppszValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + ULONG ulActualSize = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); + if (JET_wrnBufferTruncated == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record"); + + hr = StrAlloc(ppszValue, ulActualSize); + ExitOnFailure(hr, "Failed to allocate string while retrieving column value"); + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record"); + +LExit: + return hr; +} + +HRESULT DAPI EseBeginQuery( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ESE_QUERY_TYPE qtQueryType, + __out ESE_QUERY_HANDLE *peqhHandle + ) +{ + UNREFERENCED_PARAMETER(jsSession); + UNREFERENCED_PARAMETER(jtTable); + + HRESULT hr = S_OK; + + *peqhHandle = static_cast(MemAlloc(sizeof(ESE_QUERY), TRUE)); + ExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query"); + + ESE_QUERY *peqHandle = static_cast(*peqhHandle); + peqHandle->qtQueryType = qtQueryType; + peqHandle->jsSession = jsSession; + peqHandle->jtTable = jtTable; + +LExit: + return hr; +} + +// Utility function used by other functions to set a query column +HRESULT DAPI SetQueryColumn( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbData) const void *pvData, + __in DWORD cbData, + __in JET_GRBIT jGrb + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (peqHandle->dwColumns == countof(peqHandle->pvData)) + { + hr = E_NOTIMPL; + ExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData)); + } + + if (0 == peqHandle->dwColumns) // If it's the first column, start a new key + { + jGrb = jGrb | JET_bitNewKey; + } + + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb); + ExitOnJetFailure(jEr, hr, "Failed to begin new query"); + + // If the query is wildcard, setup the cached copy of pvData + if (ESE_QUERY_EXACT != peqHandle->qtQueryType) + { + peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE); + ExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory"); + + memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData); + + peqHandle->cbData[peqHandle->dwColumns] = cbData; + } + + // Increment the number of total columns + ++peqHandle->dwColumns; + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnBinary( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ) +{ + HRESULT hr = S_OK; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (cbBuffer > DWORD_MAX) + { + ExitFunction1(hr = E_INVALIDARG); + } + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, reinterpret_cast(pbBuffer), static_cast(cbBuffer), jGrb); + ExitOnFailure(hr, "Failed to set value of query colum (as binary) to:"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnDword( + __in ESE_QUERY_HANDLE eqhHandle, + __in DWORD dwData, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb); + ExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnBool( + __in ESE_QUERY_HANDLE eqhHandle, + __in BOOL fValue, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + BYTE bByte = fValue ? 0xFF : 0x00; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb); + ExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnString( + __in ESE_QUERY_HANDLE eqhHandle, + __in_z LPCWSTR pszString, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + DWORD dwStringSize = 0; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + dwStringSize = sizeof(WCHAR) * (lstrlenW(pszString) + 1); // Add 1 for null terminator + + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb); + ExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString); + +LExit: + return hr; +} + +HRESULT DAPI EseFinishQuery( + __in ESE_QUERY_HANDLE eqhHandle + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (peqHandle->fIndexRangeSet) + { + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove); + ExitOnJetFailure(jEr, hr, "Failed to release index range"); + + peqHandle->fIndexRangeSet = FALSE; + } + + for (int i=0; i < countof(peqHandle->pvData); ++i) + { + ReleaseMem(peqHandle->pvData[i]); + } + + ReleaseMem(peqHandle); + +LExit: + return hr; +} + +HRESULT DAPI EseRunQuery( + __in ESE_QUERY_HANDLE eqhHandle + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jGrb = 0; + JET_GRBIT jGrbSeekType = 0; + DWORD i; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (ESE_QUERY_EXACT == peqHandle->qtQueryType) + { + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ); + ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table"); + } + else + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrbSeekType = JET_bitSeekGE; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrbSeekType = JET_bitSeekLE; + } + + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType); + if (jEr == JET_wrnSeekNotEqual) + { + jEr = JET_errSuccess; + } + + // At this point we've already set our cursor to the beginning of the range of records to select. + // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange() + // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx + for (i = 0; i < peqHandle->dwColumns; ++i) + { + if (i == 0) + { + jGrb = JET_bitNewKey; + } + else + { + jGrb = 0; + } + + // On the last iteration + if (i == peqHandle->dwColumns - 1) + { + jGrb |= JET_bitFullColumnEndLimit; + } + + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb); + ExitOnJetFailure(jEr, hr, "Failed to begin new query"); + } + + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit); + ExitOnJetFailure(jEr, hr, "Failed to set index range"); + + peqHandle->fIndexRangeSet = TRUE; + + // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does) + // So, let's check if there is a current record before returning (by reading the first byte of one). + jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0); + ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query"); + } + +LExit: + return hr; +} diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp new file mode 100644 index 00000000..8666da65 --- /dev/null +++ b/src/dutil/fileutil.cpp @@ -0,0 +1,1860 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// constants + +const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; +const BYTE UTF16BOM[] = {0xFF, 0xFE}; + +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; + +/******************************************************************* + FileFromPath - returns a pointer to the file part of the path + +********************************************************************/ +extern "C" LPWSTR DAPI FileFromPath( + __in LPCWSTR wzPath + ) +{ + if (!wzPath) + return NULL; + + LPWSTR wzFile = const_cast(wzPath); + for (LPWSTR wz = wzFile; *wz; ++wz) + { + // valid delineators + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || L':' == *wz) + wzFile = wz + 1; + } + + return wzFile; +} + + +/******************************************************************* + FileResolvePath - gets the full path to a file resolving environment + variables along the way. + +********************************************************************/ +extern "C" HRESULT DAPI FileResolvePath( + __in LPCWSTR wzRelativePath, + __out LPWSTR *ppwzFullPath + ) +{ + Assert(wzRelativePath && *wzRelativePath); + + HRESULT hr = S_OK; + DWORD cch = 0; + LPWSTR pwzExpandedPath = NULL; + DWORD cchExpandedPath = 0; + + LPWSTR pwzFullPath = NULL; + DWORD cchFullPath = 0; + + LPWSTR wzFileName = NULL; + + // + // First, expand any environment variables. + // + cchExpandedPath = MAX_PATH; + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); + ExitOnFailure(hr, "Failed to allocate space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + cchExpandedPath = cch; + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); + ExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + } + } + + // + // Second, get the full path. + // + cchFullPath = MAX_PATH; + hr = StrAlloc(&pwzFullPath, cchFullPath); + ExitOnFailure(hr, "Failed to allocate space for full path."); + + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + } + else if (cchFullPath < cch) + { + cchFullPath = cch; + hr = StrAlloc(&pwzFullPath, cchFullPath); + ExitOnFailure(hr, "Failed to re-allocate more space for full path."); + + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + } + else if (cchFullPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + } + } + + *ppwzFullPath = pwzFullPath; + pwzFullPath = NULL; + +LExit: + ReleaseStr(pwzFullPath); + ReleaseStr(pwzExpandedPath); + + return hr; +} + + +/******************************************************************* +FileStripExtension - Strip extension from filename +********************************************************************/ +extern "C" HRESULT DAPI FileStripExtension( +__in LPCWSTR wzFileName, +__out LPWSTR *ppwzFileNameNoExtension +) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + + SIZE_T cchFileName = wcslen(wzFileName); + + LPWSTR pwzFileNameNoExtension = NULL; + DWORD cchFileNameNoExtension = 0; + + // Filename without extension can not be longer than _MAX_FNAME + // Filename without extension should also not be longer than filename itself + if (_MAX_FNAME > cchFileName) + { + cchFileNameNoExtension = (DWORD) cchFileName; + } + else + { + cchFileNameNoExtension = _MAX_FNAME; + } + + hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); + ExitOnFailure(hr, "failed to allocate space for File Name without extension"); + + // _wsplitpath_s can handle drive/path/filename/extension + errno_t err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); + if (0 != err) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to parse filename: %ls", wzFileName); + } + + *ppwzFileNameNoExtension = pwzFileNameNoExtension; + pwzFileNameNoExtension = NULL; + +LExit: + ReleaseStr(pwzFileNameNoExtension); + + return hr; +} + + +/******************************************************************* +FileChangeExtension - Changes the extension of a filename +********************************************************************/ +extern "C" HRESULT DAPI FileChangeExtension( + __in LPCWSTR wzFileName, + __in LPCWSTR wzNewExtension, + __out LPWSTR *ppwzFileNameNewExtension + ) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + LPWSTR sczFileName = NULL; + + hr = FileStripExtension(wzFileName, &sczFileName); + ExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); + + hr = StrAllocConcat(&sczFileName, wzNewExtension, 0); + ExitOnFailure(hr, "Failed to add new extension."); + + *ppwzFileNameNewExtension = sczFileName; + sczFileName = NULL; + +LExit: + ReleaseStr(sczFileName); + + return hr; +} + + +/******************************************************************* +FileAddSuffixToBaseName - Adds a suffix the base portion of a file +name; e.g., file.ext to fileSuffix.ext. +********************************************************************/ +extern "C" HRESULT DAPI FileAddSuffixToBaseName( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzSuffix, + __out_z LPWSTR* psczNewFileName + ) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + LPWSTR sczNewFileName = NULL; + + LPCWSTR wzExtension = wzFileName + lstrlenW(wzFileName); + while (wzFileName < wzExtension && L'.' != *wzExtension) + { + --wzExtension; + } + + if (wzFileName < wzExtension) + { + // found an extension so add the suffix before it + hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension); + } + else + { + // no extension, so add the suffix at the end of the whole name + hr = StrAllocString(&sczNewFileName, wzFileName, 0); + ExitOnFailure(hr, "Failed to allocate new file name."); + + hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0); + } + ExitOnFailure(hr, "Failed to allocate new file name with suffix."); + + *psczNewFileName = sczNewFileName; + sczNewFileName = NULL; + +LExit: + ReleaseStr(sczNewFileName); + + return hr; +} + + +/******************************************************************* + FileVersion + +********************************************************************/ +extern "C" HRESULT DAPI FileVersion( + __in LPCWSTR wzFilename, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ) +{ + HRESULT hr = S_OK; + + DWORD dwHandle = 0; + UINT cbVerBuffer = 0; + LPVOID pVerBuffer = NULL; + VS_FIXEDFILEINFO* pvsFileInfo = NULL; + UINT cbFileInfo = 0; + + if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle))) + { + ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + } + + pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer); + ExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); + + if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer)) + { + ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + } + + if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo)) + { + ExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); + } + + *pdwVerMajor = pvsFileInfo->dwFileVersionMS; + *pdwVerMinor = pvsFileInfo->dwFileVersionLS; + +LExit: + if (pVerBuffer) + { + ::GlobalFree(pVerBuffer); + } + return hr; +} + + +/******************************************************************* + FileVersionFromString + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionFromString( + __in LPCWSTR wzVersion, + __out DWORD* pdwVerMajor, + __out DWORD* pdwVerMinor + ) +{ + Assert(pdwVerMajor && pdwVerMinor); + + HRESULT hr = S_OK; + LPCWSTR pwz = wzVersion; + DWORD dw; + + *pdwVerMajor = 0; + *pdwVerMinor = 0; + + if ((L'v' == *pwz) || (L'V' == *pwz)) + { + ++pwz; + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMajor = dw << 16; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMajor |= dw; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMinor = dw << 16; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && L'\0' == *pwz && dw < 0x10000) + { + *pdwVerMinor |= dw; + } + else + { + ExitFunction1(hr = S_FALSE); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileVersionFromStringEx + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionFromStringEx( + __in LPCWSTR wzVersion, + __in DWORD cchVersion, + __out DWORD64* pqwVersion + ) +{ + Assert(wzVersion); + Assert(pqwVersion); + + HRESULT hr = S_OK; + LPCWSTR wzEnd = NULL; + LPCWSTR wzPartBegin = wzVersion; + LPCWSTR wzPartEnd = wzVersion; + DWORD iPart = 0; + USHORT us = 0; + DWORD64 qwVersion = 0; + + // get string length if not provided + if (0 >= cchVersion) + { + cchVersion = lstrlenW(wzVersion); + if (0 >= cchVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + if ((L'v' == *wzVersion) || (L'V' == *wzVersion)) + { + ++wzVersion; + --cchVersion; + wzPartBegin = wzVersion; + wzPartEnd = wzVersion; + } + + // save end pointer + wzEnd = wzVersion + cchVersion; + + // loop through parts + for (;;) + { + if (4 <= iPart) + { + // error, too many parts + ExitFunction1(hr = E_INVALIDARG); + } + + // find end of part + while (wzPartEnd < wzEnd && L'.' != *wzPartEnd) + { + ++wzPartEnd; + } + if (wzPartBegin == wzPartEnd) + { + // error, empty part + ExitFunction1(hr = E_INVALIDARG); + } + + DWORD cchPart; + hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); + ExitOnFailure(hr, "Version number part was too long."); + + // parse version part + hr = StrStringToUInt16(wzPartBegin, cchPart, &us); + ExitOnFailure(hr, "Failed to parse version number part."); + + // add part to qword version + qwVersion |= (DWORD64)us << ((3 - iPart) * 16); + + if (wzPartEnd >= wzEnd) + { + // end of string + break; + } + + wzPartBegin = ++wzPartEnd; // skip over separator + ++iPart; + } + + *pqwVersion = qwVersion; + +LExit: + return hr; +} + +/******************************************************************* + FileVersionFromStringEx - Formats the DWORD64 as a string version. + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionToStringEx( + __in DWORD64 qwVersion, + __out LPWSTR* psczVersion + ) +{ + HRESULT hr = S_OK; + WORD wMajor = 0; + WORD wMinor = 0; + WORD wBuild = 0; + WORD wRevision = 0; + + // Mask and shift each WORD for each field. + wMajor = (WORD)(qwVersion >> 48 & 0xffff); + wMinor = (WORD)(qwVersion >> 32 & 0xffff); + wBuild = (WORD)(qwVersion >> 16 & 0xffff); + wRevision = (WORD)(qwVersion & 0xffff); + + // Format and return the version string. + hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision); + ExitOnFailure(hr, "Failed to allocate and format the version number."); + +LExit: + return hr; +} + +/******************************************************************* + FileSetPointer - sets the file pointer. + +********************************************************************/ +extern "C" HRESULT DAPI FileSetPointer( + __in HANDLE hFile, + __in DWORD64 dw64Move, + __out_opt DWORD64* pdw64NewPosition, + __in DWORD dwMoveMethod + ) +{ + Assert(INVALID_HANDLE_VALUE != hFile); + + HRESULT hr = S_OK; + LARGE_INTEGER liMove; + LARGE_INTEGER liNewPosition; + + liMove.QuadPart = dw64Move; + if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) + { + ExitWithLastError(hr, "Failed to set file pointer."); + } + + if (pdw64NewPosition) + { + *pdw64NewPosition = liNewPosition.QuadPart; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileSize + +********************************************************************/ +extern "C" HRESULT DAPI FileSize( + __in LPCWSTR pwzFileName, + __out LONGLONG* pllSize + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + ExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); + + hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); + } + + hr = FileSizeByHandle(hFile, pllSize); + ExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + + +/******************************************************************* + FileSizeByHandle + +********************************************************************/ +extern "C" HRESULT DAPI FileSizeByHandle( + __in HANDLE hFile, + __out LONGLONG* pllSize + ) +{ + Assert(INVALID_HANDLE_VALUE != hFile && pllSize); + HRESULT hr = S_OK; + LARGE_INTEGER li; + + *pllSize = 0; + + if (!::GetFileSizeEx(hFile, &li)) + { + ExitWithLastError(hr, "Failed to get size of file."); + } + + *pllSize = li.QuadPart; + +LExit: + return hr; +} + + +/******************************************************************* + FileExistsEx + +********************************************************************/ +extern "C" BOOL DAPI FileExistsEx( + __in LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + Assert(wzPath && *wzPath); + BOOL fExists = FALSE; + + WIN32_FIND_DATAW fd = { }; + HANDLE hff; + + if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd))) + { + ::FindClose(hff); + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + if (pdwAttributes) + { + *pdwAttributes = fd.dwFileAttributes; + } + + fExists = TRUE; + } + } + + return fExists; +} + + +/******************************************************************* + FileExistsAfterRestart - checks that a file exists and will continue + to exist after restart. + +********************************************************************/ +extern "C" BOOL DAPI FileExistsAfterRestart( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + HRESULT hr = S_OK; + BOOL fExists = FALSE; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + int nCompare = 0; + + fExists = FileExistsEx(wzPath, pdwAttributes); + if (fExists) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + // Skip the long path designator if present. + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) + { + wzRename += 4; + } + + hr = PathCompare(wzPath, wzRename, &nCompare); + ExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + if (CSTR_EQUAL == nCompare) + { + fExists = FALSE; + break; + } + } + } + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return fExists; +} + + +/******************************************************************* + FileRemoveFromPendingRename - removes the file path from the pending + file rename list. + +********************************************************************/ +extern "C" HRESULT DAPI FileRemoveFromPendingRename( + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + int nCompare = 0; + BOOL fRemoved = FALSE; + DWORD cNewRenames = 0; + + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + // Skip the long path designator if present. + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) + { + wzRename += 4; + } + + hr = PathCompare(wzPath, wzRename, &nCompare); + ExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + // If we find our path in the list, null out the source and target slot and + // we'll compact the array next. + if (CSTR_EQUAL == nCompare) + { + ReleaseNullStr(rgsczRenames[i]); + ReleaseNullStr(rgsczRenames[i + 1]); + fRemoved = TRUE; + } + } + } + + if (fRemoved) + { + // Compact the array by removing any nulls. + for (DWORD i = 0; i < cRenames; ++i) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename) + { + rgsczRenames[cNewRenames] = wzRename; + ++cNewRenames; + } + } + + cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. + + // Write the new array back to the pending file rename key. + hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); + ExitOnFailure(hr, "Failed to update pending file renames."); + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return hr; +} + + +/******************************************************************* + FileRead - read a file into memory + +********************************************************************/ +extern "C" HRESULT DAPI FileRead( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out DWORD* pcbDest, + __in LPCWSTR wzSrcPath + ) +{ + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE); + return hr; +} + +/******************************************************************* + FileRead - read a file into memory with specified share mode + +********************************************************************/ +extern "C" HRESULT DAPI FileReadEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out DWORD* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD dwShareMode + ) +{ + HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode); + return hr; +} + +/******************************************************************* + FileReadUntil - read a file into memory with a maximum size + +********************************************************************/ +extern "C" HRESULT DAPI FileReadUntil( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) DWORD* pcbDest, + __in LPCWSTR wzSrcPath, + __in DWORD cbMaxRead + ) +{ + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE); + return hr; +} + + +/******************************************************************* + FileReadPartial - read a portion of a file into memory + +********************************************************************/ +extern "C" HRESULT DAPI FileReadPartial( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) DWORD* pcbDest, + __in LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK + ) +{ + return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE); +} + +/******************************************************************* + FileReadPartial - read a portion of a file into memory + (with specified share mode) +********************************************************************/ +extern "C" HRESULT DAPI FileReadPartialEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) DWORD* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK, + __in DWORD dwShareMode + ) +{ + HRESULT hr = S_OK; + + UINT er = ERROR_SUCCESS; + HANDLE hFile = INVALID_HANDLE_VALUE; + LARGE_INTEGER liFileSize = { }; + DWORD cbData = 0; + BYTE* pbData = NULL; + + ExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); + ExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); + ExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); + ExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); + + hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + er = ::GetLastError(); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); + } + + if (!::GetFileSizeEx(hFile, &liFileSize)) + { + ExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); + } + + if (fSeek) + { + if (cbStartPosition > liFileSize.QuadPart) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Start position %d bigger than file '%ls' size %d", cbStartPosition, wzSrcPath, liFileSize.QuadPart); + } + + DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT); + if (INVALID_SET_FILE_POINTER == dwErr) + { + ExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); + } + } + else + { + cbStartPosition = 0; + } + + if (fPartialOK) + { + cbData = cbMaxRead; + } + else + { + cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD + if (cbMaxRead < liFileSize.QuadPart - cbStartPosition) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); + } + } + + if (*ppbDest) + { + if (0 == cbData) + { + ReleaseNullMem(*ppbDest); + *pcbDest = 0; + ExitFunction1(hr = S_OK); + } + + LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); + + pbData = static_cast(pv); + } + else + { + if (0 == cbData) + { + *pcbDest = 0; + ExitFunction1(hr = S_OK); + } + + pbData = static_cast(MemAlloc(cbData, TRUE)); + ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); + } + + DWORD cbTotalRead = 0; + DWORD cbRead = 0; + do + { + DWORD cbRemaining = 0; + hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining); + ExitOnFailure(hr, "Underflow calculating remaining buffer size."); + + if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); + } + + cbTotalRead += cbRead; + } while (cbRead); + + if (cbTotalRead != cbData) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); + } + + *ppbDest = pbData; + pbData = NULL; + *pcbDest = cbData; + +LExit: + ReleaseMem(pbData); + ReleaseFile(hFile); + + return hr; +} + + +/******************************************************************* + FileWrite - write a file from memory + +********************************************************************/ +extern "C" HRESULT DAPI FileWrite( + __in_z LPCWSTR pwzFileName, + __in DWORD dwFlagsAndAttributes, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in DWORD cbData, + __out_opt HANDLE* pHandle + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Open the file + hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); + + hr = FileWriteHandle(hFile, pbData, cbData); + ExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); + + if (pHandle) + { + *pHandle = hFile; + hFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hFile); + + return hr; +} + + +/******************************************************************* + FileWriteHandle - write to a file handle from memory + +********************************************************************/ +extern "C" HRESULT DAPI FileWriteHandle( + __in HANDLE hFile, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + DWORD cbDataWritten = 0; + DWORD cbTotal = 0; + + // Write out all of the data. + do + { + if (!::WriteFile(hFile, pbData + cbTotal, cbData - cbTotal, &cbDataWritten, NULL)) + { + ExitOnLastError(hr, "Failed to write data to file handle."); + } + + cbTotal += cbDataWritten; + } while (cbTotal < cbData); + +LExit: + return hr; +} + + +/******************************************************************* + FileCopyUsingHandles + +*******************************************************************/ +extern "C" HRESULT DAPI FileCopyUsingHandles( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __out_opt DWORD64* pcbCopied + ) +{ + HRESULT hr = S_OK; + DWORD64 cbTotalCopied = 0; + BYTE rgbData[4 * 1024]; + DWORD cbRead = 0; + + do + { + cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); + if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read from source."); + } + + if (cbRead) + { + hr = FileWriteHandle(hTarget, rgbData, cbRead); + ExitOnFailure(hr, "Failed to write to target."); + } + + cbTotalCopied += cbRead; + } while (cbTotalCopied < cbCopy && 0 != cbRead); + + if (pcbCopied) + { + *pcbCopied = cbTotalCopied; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureCopy + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureCopy( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite + ) +{ + HRESULT hr = S_OK; + DWORD er; + + // try to copy the file first + if (::CopyFileW(wzSource, wzTarget, !fOverwrite)) + { + ExitFunction(); // we're done + } + + er = ::GetLastError(); // check the error and do the right thing below + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) + { + // if not overwriting this is an expected error + ExitFunction1(hr = S_FALSE); + } + else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist + { + // try to create the directory then do the copy + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + if (pwzLastSlash) + { + *pwzLastSlash = L'\0'; // null terminate + hr = DirEnsureExists(wzTarget, NULL); + *pwzLastSlash = L'\\'; // now put the slash back + ExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); + + // try to copy again + if (!::CopyFileW(wzSource, wzTarget, fOverwrite)) + { + ExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); + } + } + else // no path was specified so just return the error + { + hr = HRESULT_FROM_WIN32(er); + } + } + else // unexpected error + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureCopyWithRetry + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureCopyWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); + + HRESULT hr = E_FAIL; + DWORD i = 0; + + for (i = 0; FAILED(hr) && i <= cRetry; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr + || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr) + { + break; // no reason to retry these errors. + } + } + ExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureMove + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureMove( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy + ) +{ + HRESULT hr = S_OK; + DWORD er; + + DWORD dwFlags = 0; + + if (fOverwrite) + { + dwFlags |= MOVEFILE_REPLACE_EXISTING; + } + if (fAllowCopy) + { + dwFlags |= MOVEFILE_COPY_ALLOWED; + } + + // try to move the file first + if (::MoveFileExW(wzSource, wzTarget, dwFlags)) + { + ExitFunction(); // we're done + } + + er = ::GetLastError(); // check the error and do the right thing below + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) + { + // if not overwriting this is an expected error + ExitFunction1(hr = S_FALSE); + } + else if (ERROR_FILE_NOT_FOUND == er) + { + // We are seeing some cases where ::MoveFileEx() says a file was not found + // but the source file is actually present. In that case, return path not + // found so we try to create the target path since that is most likely + // what is missing. Otherwise, the source file is missing and we're obviously + // not going to be recovering from that. + if (FileExistsEx(wzSource, NULL)) + { + er = ERROR_PATH_NOT_FOUND; + } + } + + // If the path doesn't exist, try to create the directory tree then do the move. + if (ERROR_PATH_NOT_FOUND == er) + { + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + if (pwzLastSlash) + { + *pwzLastSlash = L'\0'; // null terminate + hr = DirEnsureExists(wzTarget, NULL); + *pwzLastSlash = L'\\'; // now put the slash back + ExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); + + // try to move again + if (!::MoveFileExW(wzSource, wzTarget, dwFlags)) + { + ExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); + } + } + else // no path was specified so just return the error + { + hr = HRESULT_FROM_WIN32(er); + } + } + else // unexpected error + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureMoveWithRetry + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureMoveWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); + + HRESULT hr = E_FAIL; + DWORD i = 0; + + for (i = 0; FAILED(hr) && i < cRetry + 1; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy); + } + ExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + +LExit: + return hr; +} + + +/******************************************************************* + FileCreateTemp - creates an empty temp file + + NOTE: uses ANSI functions internally so it is Win9x safe +********************************************************************/ +extern "C" HRESULT DAPI FileCreateTemp( + __in LPCWSTR wzPrefix, + __in LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ) +{ + Assert(wzPrefix && *wzPrefix); + HRESULT hr = S_OK; + LPSTR pszTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + LPSTR pszTempFile = NULL; + + int i = 0; + + hr = StrAnsiAlloc(&pszTempPath, cchTempPath); + ExitOnFailure(hr, "failed to allocate memory for the temp path"); + ::GetTempPathA(cchTempPath, pszTempPath); + + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension); + ExitOnFailure(hr, "failed to allocate memory for log file"); + + hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + continue; + } + ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pszTempFile); + } + } + + if (ppwzTempFile) + { + hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(pszTempFile); + ReleaseStr(pszTempPath); + + return hr; +} + + +/******************************************************************* + FileCreateTempW - creates an empty temp file + +*******************************************************************/ +extern "C" HRESULT DAPI FileCreateTempW( + __in LPCWSTR wzPrefix, + __in LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ) +{ + Assert(wzPrefix && *wzPrefix); + HRESULT hr = E_FAIL; + + WCHAR wzTempPath[MAX_PATH]; + DWORD cchTempPath = countof(wzTempPath); + LPWSTR pwzTempFile = NULL; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + int i = 0; + + if (!::GetTempPathW(cchTempPath, wzTempPath)) + { + ExitOnLastError(hr, "failed to get temp path"); + } + + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); + ExitOnFailure(hr, "failed to allocate memory for temp filename"); + + hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + continue; + } + ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); + } + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + + if (ppwzTempFile) + { + *ppwzTempFile = pwzTempFile; + pwzTempFile = NULL; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(pwzTempFile); + + return hr; +} + + +/******************************************************************* + FileIsSame + +********************************************************************/ +extern "C" HRESULT DAPI FileIsSame( + __in LPCWSTR wzFile1, + __in LPCWSTR wzFile2, + __out LPBOOL lpfSameFile + ) +{ + HRESULT hr = S_OK; + HANDLE hFile1 = NULL; + HANDLE hFile2 = NULL; + BY_HANDLE_FILE_INFORMATION fileInfo1 = { }; + BY_HANDLE_FILE_INFORMATION fileInfo2 = { }; + + hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + ExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); + + hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + ExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); + + if (!::GetFileInformationByHandle(hFile1, &fileInfo1)) + { + ExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); + } + + if (!::GetFileInformationByHandle(hFile2, &fileInfo2)) + { + ExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); + } + + *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber && + fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh && + fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE; + +LExit: + ReleaseFile(hFile1); + ReleaseFile(hFile2); + + return hr; +} + +/******************************************************************* + FileEnsureDelete - deletes a file, first removing read-only, + hidden, or system attributes if necessary. +********************************************************************/ +extern "C" HRESULT DAPI FileEnsureDelete( + __in LPCWSTR wzFile + ) +{ + HRESULT hr = S_OK; + + DWORD dwAttrib = INVALID_FILE_ATTRIBUTES; + if (FileExistsEx(wzFile, &dwAttrib)) + { + if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM) + { + if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL)) + { + ExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); + } + } + + if (!::DeleteFileW(wzFile)) + { + ExitOnLastError(hr, "Failed to delete file: %ls", wzFile); + } + } + +LExit: + return hr; +} + +/******************************************************************* + FileGetTime - Gets the file time of a specified file +********************************************************************/ +extern "C" HRESULT DAPI FileGetTime( + __in LPCWSTR wzFile, + __out_opt LPFILETIME lpCreationTime, + __out_opt LPFILETIME lpLastAccessTime, + __out_opt LPFILETIME lpLastWriteTime + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + + hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) + { + ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + +/******************************************************************* + FileSetTime - Sets the file time of a specified file +********************************************************************/ +extern "C" HRESULT DAPI FileSetTime( + __in LPCWSTR wzFile, + __in_opt const FILETIME *lpCreationTime, + __in_opt const FILETIME *lpLastAccessTime, + __in_opt const FILETIME *lpLastWriteTime + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) + { + ExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + +/******************************************************************* + FileReSetTime - ReSets a file's last acess and modified time to the + creation time of the file +********************************************************************/ +extern "C" HRESULT DAPI FileResetTime( + __in LPCWSTR wzFile + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + FILETIME ftCreateTime; + + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + } + + if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime)) + { + ExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + + +/******************************************************************* + FileExecutableArchitecture + +*******************************************************************/ +extern "C" HRESULT DAPI FileExecutableArchitecture( + __in LPCWSTR wzFile, + __out FILE_ARCHITECTURE *pArchitecture + ) +{ + HRESULT hr = S_OK; + + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD cbRead = 0; + IMAGE_DOS_HEADER DosImageHeader = { }; + IMAGE_NT_HEADERS NtImageHeader = { }; + + hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (hFile == INVALID_HANDLE_VALUE) + { + ExitWithLastError(hr, "Failed to open file: %ls", wzFile); + } + + if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); + } + + if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + ExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); + } + + if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); + } + + if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); + } + + if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + ExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); + } + + if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem || + IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem || + IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem) + { + switch (NtImageHeader.FileHeader.Machine) + { + case IMAGE_FILE_MACHINE_I386: + *pArchitecture = FILE_ARCHITECTURE_X86; + break; + case IMAGE_FILE_MACHINE_IA64: + *pArchitecture = FILE_ARCHITECTURE_IA64; + break; + case IMAGE_FILE_MACHINE_AMD64: + *pArchitecture = FILE_ARCHITECTURE_X64; + break; + default: + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + break; + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + } + ExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); + +LExit: + if (hFile != INVALID_HANDLE_VALUE) + { + ::CloseHandle(hFile); + } + + return hr; +} + +/******************************************************************* + FileToString + +*******************************************************************/ +extern "C" HRESULT DAPI FileToString( + __in_z LPCWSTR wzFile, + __out LPWSTR *psczString, + __out_opt FILE_ENCODING *pfeEncoding + ) +{ + HRESULT hr = S_OK; + BYTE *pbFullFileBuffer = NULL; + DWORD cbFullFileBuffer = 0; + BOOL fNullCharFound = FALSE; + LPWSTR sczFileText = NULL; + + // Check if the file is ANSI + hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile); + ExitOnFailure(hr, "Failed to read file: %ls", wzFile); + + if (0 == cbFullFileBuffer) + { + *psczString = NULL; + ExitFunction1(hr = S_OK); + } + + // UTF-8 BOM + if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM))) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM; + } + + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8); + ExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); + + *psczString = sczFileText; + sczFileText = NULL; + } + // UTF-16 BOM, little endian (windows regular UTF-16) + else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM))) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM; + } + + hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate copy of string"); + } + // No BOM, let's try to detect + else + { + for (DWORD i = 0; i < cbFullFileBuffer; ++i) + { + if (pbFullFileBuffer[i] == '\0') + { + fNullCharFound = TRUE; + break; + } + } + + if (!fNullCharFound) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF8; + } + + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8); + if (FAILED(hr)) + { + if (E_OUTOFMEMORY == hr) + { + ExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); + } + } + else + { + *psczString = sczFileText; + sczFileText = NULL; + } + } + else if (NULL == *psczString) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF16; + } + + hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate copy of string"); + } + } + +LExit: + ReleaseStr(sczFileText); + ReleaseMem(pbFullFileBuffer); + + return hr; +} + +/******************************************************************* + FileFromString + +*******************************************************************/ +extern "C" HRESULT DAPI FileFromString( + __in_z LPCWSTR wzFile, + __in DWORD dwFlagsAndAttributes, + __in_z LPCWSTR sczString, + __in FILE_ENCODING feEncoding + ) +{ + HRESULT hr = S_OK; + LPSTR sczUtf8String = NULL; + BYTE *pbFullFileBuffer = NULL; + const BYTE *pcbFullFileBuffer = NULL; + DWORD cbFullFileBuffer = 0; + DWORD cbStrLen = 0; + + switch (feEncoding) + { + case FILE_ENCODING_UTF8: + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + + cbFullFileBuffer = lstrlenA(sczUtf8String); + pcbFullFileBuffer = reinterpret_cast(sczUtf8String); + break; + case FILE_ENCODING_UTF8_WITH_BOM: + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + + cbStrLen = lstrlenA(sczUtf8String); + cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; + + pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); + ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + + memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM)); + memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen); + pcbFullFileBuffer = pbFullFileBuffer; + break; + case FILE_ENCODING_UTF16: + cbFullFileBuffer = lstrlenW(sczString) * sizeof(WCHAR); + pcbFullFileBuffer = reinterpret_cast(sczString); + break; + case FILE_ENCODING_UTF16_WITH_BOM: + cbStrLen = lstrlenW(sczString) * sizeof(WCHAR); + cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; + + pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); + ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + + memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM)); + memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen); + pcbFullFileBuffer = pbFullFileBuffer; + break; + } + + hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL); + ExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); + +LExit: + ReleaseStr(sczUtf8String); + ReleaseMem(pbFullFileBuffer); + + return hr; +} diff --git a/src/dutil/gdiputil.cpp b/src/dutil/gdiputil.cpp new file mode 100644 index 00000000..aef6178f --- /dev/null +++ b/src/dutil/gdiputil.cpp @@ -0,0 +1,212 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace Gdiplus; + +/******************************************************************** + GdipInitialize - initializes GDI+. + + Note: pOutput must be non-NULL if pInput->SuppressBackgroundThread + is TRUE. See GdiplusStartup() for more information. +********************************************************************/ +extern "C" HRESULT DAPI GdipInitialize( + __in const Gdiplus::GdiplusStartupInput* pInput, + __out ULONG_PTR* pToken, + __out_opt Gdiplus::GdiplusStartupOutput *pOutput + ) +{ + AssertSz(!pInput->SuppressBackgroundThread || pOutput, "pOutput required if background thread suppressed."); + + HRESULT hr = S_OK; + Status status = Ok; + + status = GdiplusStartup(pToken, pInput, pOutput); + ExitOnGdipFailure(status, hr, "Failed to initialize GDI+."); + +LExit: + return hr; +} + +/******************************************************************** + GdipUninitialize - uninitializes GDI+. + +********************************************************************/ +extern "C" void DAPI GdipUninitialize( + __in ULONG_PTR token + ) +{ + GdiplusShutdown(token); +} + +/******************************************************************** + GdipBitmapFromResource - read a GDI+ image out of a resource stream + +********************************************************************/ +extern "C" HRESULT DAPI GdipBitmapFromResource( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szId, + __out Bitmap **ppBitmap + ) +{ + HRESULT hr = S_OK; + LPVOID pvData = NULL; + DWORD cbData = 0; + HGLOBAL hGlobal = NULL;; + LPVOID pv = NULL; + IStream *pStream = NULL; + Bitmap *pBitmap = NULL; + Status gs = Ok; + + hr = ResReadData(hinst, szId, &pvData, &cbData); + ExitOnFailure(hr, "Failed to load GDI+ bitmap from resource."); + + // Have to copy the fixed resource data into moveable (heap) memory + // since that's what GDI+ expects. + hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData); + ExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory."); + + pv = ::GlobalLock(hGlobal); + ExitOnNullWithLastError(pv, hr, "Failed to lock global memory."); + + memcpy(pv, pvData, cbData); + + ::GlobalUnlock(pv); // no point taking any more memory than we have already + pv = NULL; + + hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); + ExitOnFailure(hr, "Failed to allocate stream from global memory."); + + hGlobal = NULL; // we gave the global memory to the stream object so it will close it + + pBitmap = Bitmap::FromStream(pStream); + ExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream."); + + gs = pBitmap->GetLastStatus(); + ExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream."); + + *ppBitmap = pBitmap; + pBitmap = NULL; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + ReleaseObject(pStream); + + if (pv) + { + ::GlobalUnlock(pv); + } + + if (hGlobal) + { + ::GlobalFree(hGlobal); + } + + return hr; +} + + +/******************************************************************** + GdipBitmapFromFile - read a GDI+ image from a file. + +********************************************************************/ +extern "C" HRESULT DAPI GdipBitmapFromFile( + __in_z LPCWSTR wzFileName, + __out Bitmap **ppBitmap + ) +{ + HRESULT hr = S_OK; + Bitmap *pBitmap = NULL; + Status gs = Ok; + + ExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName"); + + pBitmap = Bitmap::FromFile(wzFileName); + ExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file."); + + gs = pBitmap->GetLastStatus(); + ExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName); + + *ppBitmap = pBitmap; + pBitmap = NULL; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + return hr; +} + + +HRESULT DAPI GdipHresultFromStatus( + __in Gdiplus::Status gs + ) +{ + switch (gs) + { + case Ok: + return S_OK; + + case GenericError: + return E_FAIL; + + case InvalidParameter: + return E_INVALIDARG; + + case OutOfMemory: + return E_OUTOFMEMORY; + + case ObjectBusy: + return HRESULT_FROM_WIN32(ERROR_BUSY); + + case InsufficientBuffer: + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + + case NotImplemented: + return E_NOTIMPL; + + case Win32Error: + return E_FAIL; + + case WrongState: + return E_FAIL; + + case Aborted: + return E_ABORT; + + case FileNotFound: + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + case ValueOverflow: + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + case AccessDenied: + return E_ACCESSDENIED; + + case UnknownImageFormat: + return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + + case FontFamilyNotFound: __fallthrough; + case FontStyleNotFound: __fallthrough; + case NotTrueTypeFont: + return E_UNEXPECTED; + + case UnsupportedGdiplusVersion: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + case GdiplusNotInitialized: + return E_UNEXPECTED; + + case PropertyNotFound: __fallthrough; + case PropertyNotSupported: + return E_FAIL; + } + + return E_UNEXPECTED; +} diff --git a/src/dutil/guidutil.cpp b/src/dutil/guidutil.cpp new file mode 100644 index 00000000..c0353892 --- /dev/null +++ b/src/dutil/guidutil.cpp @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +extern "C" HRESULT DAPI GuidFixedCreate( + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid + ) +{ + HRESULT hr = S_OK; + UUID guid = { }; + + hr = HRESULT_FROM_RPC(::UuidCreate(&guid)); + ExitOnFailure(hr, "UuidCreate failed."); + + if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) + { + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert guid into string."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI GuidCreate( + __deref_out_z LPWSTR* psczGuid + ) +{ + HRESULT hr = S_OK; + + hr = StrAlloc(psczGuid, GUID_STRING_LENGTH); + ExitOnFailure(hr, "Failed to allocate space for guid"); + + hr = GuidFixedCreate(*psczGuid); + ExitOnFailure(hr, "Failed to create new guid."); + +LExit: + return hr; +} diff --git a/src/dutil/iis7util.cpp b/src/dutil/iis7util.cpp new file mode 100644 index 00000000..04165a8d --- /dev/null +++ b/src/dutil/iis7util.cpp @@ -0,0 +1,518 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include "iis7util.h" + +#define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt) + +extern "C" HRESULT DAPI Iis7PutPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT vtPut + ) +{ + HRESULT hr = S_OK; + IAppHostProperty *pProperty = NULL; + BSTR bstrPropName = NULL; + + bstrPropName = ::SysAllocString(wzPropName); + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pElement->GetPropertyByName(bstrPropName, &pProperty); + ExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + + hr = pProperty->put_Value(vtPut); + ExitOnFailure(hr, "Failed to set property value for %ls", wzPropName); + +LExit: + ReleaseBSTR(bstrPropName); + // caller responsible for cleaning up variant vtPut + ReleaseObject(pProperty); + + return hr; +} + +extern "C" HRESULT DAPI Iis7PutPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_BSTR; + vtPut.bstrVal = ::SysAllocString(wzString); + ExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut); + +LExit: + ReleaseVariant(vtPut); + + return hr; +} + +extern "C" HRESULT DAPI Iis7PutPropertyInteger( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in DWORD dValue + ) +{ + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_I4; + vtPut.lVal = dValue; + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); +} + +extern "C" HRESULT DAPI Iis7PutPropertyBool( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in BOOL fValue) +{ + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_BOOL; + vtPut.boolVal = (fValue == FALSE) ? VARIANT_FALSE : VARIANT_TRUE; + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); +} + +extern "C" HRESULT DAPI Iis7GetPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT* vtGet + ) +{ + HRESULT hr = S_OK; + IAppHostProperty *pProperty = NULL; + BSTR bstrPropName = NULL; + + bstrPropName = ::SysAllocString(wzPropName); + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pElement->GetPropertyByName(bstrPropName, &pProperty); + ExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + + hr = pProperty->get_Value(vtGet); + ExitOnFailure(hr, "Failed to get property value for %ls", wzPropName); + +LExit: + ReleaseBSTR(bstrPropName); + // caller responsible for cleaning up variant vtGet + ReleaseObject(pProperty); + + return hr; +} + +extern "C" HRESULT DAPI Iis7GetPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPWSTR* psczGet + ) +{ + HRESULT hr = S_OK; + VARIANT vtGet; + + ::VariantInit(&vtGet); + hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet); + ExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName); + + if (!ISSTRINGVARIANT(vtGet.vt)) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt); + } + + hr = StrAllocString(psczGet, vtGet.bstrVal, 0); + +LExit: + ReleaseVariant(vtGet); + + return hr; +} + +BOOL DAPI CompareVariantDefault( + __in VARIANT* pVariant1, + __in VARIANT* pVariant2 + ) +{ + BOOL fEqual = FALSE; + + switch (pVariant1->vt) + { + // VarCmp doesn't work for unsigned ints + // We'd like to allow signed/unsigned comparison as well since + // IIS doesn't document variant type for integer fields + case VT_I1: + case VT_UI1: + if (VT_I1 == pVariant2->vt || VT_UI1 == pVariant2->vt) + { + fEqual = pVariant1->bVal == pVariant2->bVal; + } + break; + case VT_I2: + case VT_UI2: + if (VT_I2 == pVariant2->vt || VT_UI2 == pVariant2->vt) + { + fEqual = pVariant1->uiVal == pVariant2->uiVal; + } + break; + case VT_UI4: + case VT_I4: + if (VT_I4 == pVariant2->vt || VT_UI4 == pVariant2->vt) + { + fEqual = pVariant1->ulVal == pVariant2->ulVal; + } + break; + case VT_UI8: + case VT_I8: + if (VT_I8 == pVariant2->vt || VT_UI8 == pVariant2->vt) + { + fEqual = pVariant1->ullVal == pVariant2->ullVal; + } + break; + default: + fEqual = VARCMP_EQ == ::VarCmp(pVariant1, + pVariant2, + LOCALE_INVARIANT, + NORM_IGNORECASE); + } + + return fEqual; +} + +BOOL DAPI CompareVariantPath( + __in VARIANT* pVariant1, + __in VARIANT* pVariant2 + ) +{ + HRESULT hr = S_OK; + BOOL fEqual = FALSE; + LPWSTR wzValue1 = NULL; + LPWSTR wzValue2 = NULL; + + if (ISSTRINGVARIANT(pVariant1->vt)) + { + hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + ExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal); + } + + if (ISSTRINGVARIANT(pVariant2->vt)) + { + hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + ExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal); + } + + fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1); + +LExit: + ReleaseNullStr(wzValue1); + ReleaseNullStr(wzValue2); + return fEqual; +} + +BOOL DAPI IsMatchingAppHostElementCallback( + __in IAppHostElement *pElement, + __in_bcount(sizeof(IIS7_APPHOSTELEMENTCOMPARISON)) LPVOID pContext + ) +{ + IIS7_APPHOSTELEMENTCOMPARISON* pComparison = (IIS7_APPHOSTELEMENTCOMPARISON*) pContext; + + return Iis7IsMatchingAppHostElement(pElement, pComparison); +} + +extern "C" BOOL DAPI Iis7IsMatchingAppHostElement( + __in IAppHostElement *pElement, + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison + ) +{ + HRESULT hr = S_OK; + BOOL fResult = FALSE; + IAppHostProperty *pProperty = NULL; + BSTR bstrElementName = NULL; + + VARIANT vPropValue; + ::VariantInit(&vPropValue); + + // Use the default comparator if a comparator is not specified + VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault; + + hr = pElement->get_Name(&bstrElementName); + ExitOnFailure(hr, "Failed to get name of element"); + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1)) + { + ExitFunction(); + } + + hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue); + ExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName); + + if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue)) + { + fResult = TRUE; + } + +LExit: + ReleaseBSTR(bstrElementName); + ReleaseVariant(vPropValue); + ReleaseObject(pProperty); + + return fResult; +} + +BOOL DAPI IsMatchingAppHostMethod( + __in IAppHostMethod *pMethod, + __in LPCWSTR wzMethodName + ) +{ + HRESULT hr = S_OK; + BOOL fResult = FALSE; + BSTR bstrName = NULL; + + hr = pMethod->get_Name(&bstrName); + ExitOnFailure(hr, "Failed to get name of element"); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1)) + { + fResult = TRUE; + } + +LExit: + ReleaseBSTR(bstrName); + + return fResult; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementPath( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; + VARIANT vtValue = { }; + ::VariantInit(&vtValue); + + vtValue.vt = VT_BSTR; + vtValue.bstrVal = ::SysAllocString(wzAttributeValue); + ExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + comparison.sczElementName = wzElementName; + comparison.sczAttributeName = wzAttributeName; + comparison.pvAttributeValue = &vtValue; + comparison.pComparator = CompareVariantPath; + + hr = Iis7EnumAppHostElements(pCollection, + IsMatchingAppHostElementCallback, + &comparison, + ppElement, + pdwIndex); + +LExit: + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementString( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + VARIANT vtValue; + ::VariantInit(&vtValue); + + vtValue.vt = VT_BSTR; + vtValue.bstrVal = ::SysAllocString(wzAttributeValue); + ExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = Iis7FindAppHostElementVariant(pCollection, + wzElementName, + wzAttributeName, + &vtValue, + ppElement, + pdwIndex); + +LExit: + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementInteger( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in DWORD dwAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + VARIANT vtValue; + ::VariantInit(&vtValue); + + vtValue.vt = VT_UI4; + vtValue.ulVal = dwAttributeValue; + + hr = Iis7FindAppHostElementVariant(pCollection, + wzElementName, + wzAttributeName, + &vtValue, + ppElement, + pdwIndex); + + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementVariant( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in VARIANT* pvAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; + comparison.sczElementName = wzElementName; + comparison.sczAttributeName = wzAttributeName; + comparison.pvAttributeValue = pvAttributeValue; + comparison.pComparator = CompareVariantDefault; + + return Iis7EnumAppHostElements(pCollection, + IsMatchingAppHostElementCallback, + &comparison, + ppElement, + pdwIndex); +} + +extern "C" HRESULT DAPI Iis7EnumAppHostElements( + __in IAppHostElementCollection *pCollection, + __in ENUMAPHOSTELEMENTPROC pCallback, + __in LPVOID pContext, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pElement = NULL; + DWORD dwElements = 0; + + VARIANT vtIndex; + ::VariantInit(&vtIndex); + + if (NULL != ppElement) + { + *ppElement = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = MAXDWORD; + } + + hr = pCollection->get_Count(&dwElements); + ExitOnFailure(hr, "Failed get application IAppHostElementCollection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < dwElements; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pElement); + ExitOnFailure(hr, "Failed get IAppHostElement element"); + + if (pCallback(pElement, pContext)) + { + if (NULL != ppElement) + { + *ppElement = pElement; + pElement = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = i; + } + break; + } + + ReleaseNullObject(pElement); + } + +LExit: + ReleaseObject(pElement); + ReleaseVariant(vtIndex); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostMethod( + __in IAppHostMethodCollection *pCollection, + __in LPCWSTR wzMethodName, + __out IAppHostMethod** ppMethod, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IAppHostMethod *pMethod = NULL; + DWORD dwMethods = 0; + + VARIANT vtIndex; + ::VariantInit(&vtIndex); + + if (NULL != ppMethod) + { + *ppMethod = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = MAXDWORD; + } + + hr = pCollection->get_Count(&dwMethods); + ExitOnFailure(hr, "Failed get application IAppHostMethodCollection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < dwMethods; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pMethod); + ExitOnFailure(hr, "Failed get IAppHostMethod element"); + + if (IsMatchingAppHostMethod(pMethod, wzMethodName)) + { + if (NULL != ppMethod) + { + *ppMethod = pMethod; + pMethod = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = i; + } + break; + } + + ReleaseNullObject(pMethod); + } + +LExit: + ReleaseObject(pMethod); + ReleaseVariant(vtIndex); + + return hr; +} diff --git a/src/dutil/inc/aclutil.h b/src/dutil/inc/aclutil.h new file mode 100644 index 00000000..144e4613 --- /dev/null +++ b/src/dutil/inc/aclutil.h @@ -0,0 +1,154 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#define ReleaseSid(x) if (x) { AclFreeSid(x); } +#define ReleaseNullSid(x) if (x) { AclFreeSid(x); x = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + +// structs +struct ACL_ACCESS +{ + BOOL fDenyAccess; + DWORD dwAccessMask; + + // TODO: consider using a union + LPCWSTR pwzAccountName; // NOTE: the last three items in this structure are ignored if this is not NULL + + SID_IDENTIFIER_AUTHORITY sia; // used if pwzAccountName is NULL + BYTE nSubAuthorityCount; + DWORD nSubAuthority[8]; +}; + +struct ACL_ACE +{ + DWORD dwFlags; + DWORD dwMask; + PSID psid; +}; + + +// functions +HRESULT DAPI AclCheckAccess( + __in HANDLE hToken, + __in ACL_ACCESS* paa + ); +HRESULT DAPI AclCheckAdministratorAccess( + __in HANDLE hToken + ); +HRESULT DAPI AclCheckLocalSystemAccess( + __in HANDLE hToken + ); + +HRESULT DAPI AclGetWellKnownSid( + __in WELL_KNOWN_SID_TYPE wkst, + __deref_out PSID* ppsid + ); +HRESULT DAPI AclGetAccountSid( + __in_opt LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out PSID* ppsid + ); +HRESULT DAPI AclGetAccountSidString( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* ppwzSid + ); + +HRESULT DAPI AclCreateDacl( + __in_ecount(cDeny) ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount(cAllow) ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAcl + ); +HRESULT DAPI AclAddToDacl( + __in ACL* pAcl, + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAclNew + ); +HRESULT DAPI AclMergeDacls( + __in const ACL* pAcl1, + __in const ACL* pAcl2, + __deref_out ACL** ppAclNew + ); +HRESULT DAPI AclCreateDaclOld( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out ACL** ppAcl + ); +HRESULT DAPI AclCreateSecurityDescriptor( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclCreateSecurityDescriptorFromDacl( + __in ACL* pACL, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT __cdecl AclCreateSecurityDescriptorFromString( + __deref_out SECURITY_DESCRIPTOR** ppsd, + __in_z __format_string LPCWSTR wzSddlFormat, + ... + ); +HRESULT DAPI AclDuplicateSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclGetSecurityDescriptor( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclSetSecurityWithRetry( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __in_opt PSID psidOwner, + __in_opt PSID psidGroup, + __in_opt PACL pDacl, + __in_opt PACL pSacl, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); + +HRESULT DAPI AclFreeSid( + __in PSID psid + ); +HRESULT DAPI AclFreeDacl( + __in ACL* pACL + ); +HRESULT DAPI AclFreeSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd + ); + +HRESULT DAPI AclAddAdminToSecurityDescriptor( + __in SECURITY_DESCRIPTOR* pSecurity, + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew + ); + +// Following code in acl2util.cpp due to dependency on crypt32.dll. +HRESULT DAPI AclCalculateServiceSidString( + __in LPCWSTR wzServiceName, + __in int cchServiceName, + __deref_out_z LPWSTR* psczSid + ); +HRESULT DAPI AclGetAccountSidStringEx( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* psczSid + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/apputil.h b/src/dutil/inc/apputil.h new file mode 100644 index 00000000..1a1e14f7 --- /dev/null +++ b/src/dutil/inc/apputil.h @@ -0,0 +1,45 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// functions + +/******************************************************************** +AppFreeCommandLineArgs - frees argv from AppParseCommandLine. + +********************************************************************/ +void DAPI AppFreeCommandLineArgs( + __in LPWSTR* argv + ); + +void DAPI AppInitialize( + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], + __in DWORD cSafelyLoadSystemDlls + ); + +/******************************************************************** +AppInitializeUnsafe - initializes without the full standard safety + precautions for an application. + +********************************************************************/ +void DAPI AppInitializeUnsafe(); + +/******************************************************************** +AppParseCommandLine - parses the command line using CommandLineToArgvW. + The caller must free the value of pArgv on success + by calling AppFreeCommandLineArgs. + +********************************************************************/ +DAPI_(HRESULT) AppParseCommandLine( + __in LPCWSTR wzCommandLine, + __in int* argc, + __in LPWSTR** pArgv + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/apuputil.h b/src/dutil/inc/apuputil.h new file mode 100644 index 00000000..6764bde8 --- /dev/null +++ b/src/dutil/inc/apuputil.h @@ -0,0 +1,86 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } +#define ReleaseNullApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } + + +const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn"; + +typedef enum APUP_HASH_ALGORITHM +{ + APUP_HASH_ALGORITHM_UNKNOWN, + APUP_HASH_ALGORITHM_MD5, + APUP_HASH_ALGORITHM_SHA1, + APUP_HASH_ALGORITHM_SHA256, +} APUP_HASH_ALGORITHM; + + +struct APPLICATION_UPDATE_ENCLOSURE +{ + LPWSTR wzUrl; + LPWSTR wzLocalName; + DWORD64 dw64Size; + + BYTE* rgbDigest; + DWORD cbDigest; + APUP_HASH_ALGORITHM digestAlgorithm; + + BOOL fInstaller; +}; + + +struct APPLICATION_UPDATE_ENTRY +{ + LPWSTR wzApplicationId; + LPWSTR wzApplicationType; + LPWSTR wzTitle; + LPWSTR wzSummary; + LPWSTR wzContentType; + LPWSTR wzContent; + + LPWSTR wzUpgradeId; + BOOL fUpgradeExclusive; + DWORD64 dw64Version; + DWORD64 dw64UpgradeVersion; + + DWORD64 dw64TotalSize; + + DWORD cEnclosures; + APPLICATION_UPDATE_ENCLOSURE* rgEnclosures; +}; + + +struct APPLICATION_UPDATE_CHAIN +{ + LPWSTR wzDefaultApplicationId; + LPWSTR wzDefaultApplicationType; + + DWORD cEntries; + APPLICATION_UPDATE_ENTRY* rgEntries; +}; + + +HRESULT DAPI ApupAllocChainFromAtom( + __in ATOM_FEED* pFeed, + __out APPLICATION_UPDATE_CHAIN** ppChain + ); + +HRESULT DAPI ApupFilterChain( + __in APPLICATION_UPDATE_CHAIN* pChain, + __in DWORD64 dw64Version, + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain + ); + +void DAPI ApupFreeChain( + __in APPLICATION_UPDATE_CHAIN* pChain + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/atomutil.h b/src/dutil/inc/atomutil.h new file mode 100644 index 00000000..ff869c4a --- /dev/null +++ b/src/dutil/inc/atomutil.h @@ -0,0 +1,146 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); } +#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; } + + +struct ATOM_UNKNOWN_ATTRIBUTE +{ + LPWSTR wzNamespace; + LPWSTR wzAttribute; + LPWSTR wzValue; + + ATOM_UNKNOWN_ATTRIBUTE* pNext; +}; + +struct ATOM_UNKNOWN_ELEMENT +{ + LPWSTR wzNamespace; + LPWSTR wzElement; + LPWSTR wzValue; + + ATOM_UNKNOWN_ATTRIBUTE* pAttributes; + ATOM_UNKNOWN_ELEMENT* pNext; +}; + +struct ATOM_LINK +{ + LPWSTR wzRel; + LPWSTR wzTitle; + LPWSTR wzType; + LPWSTR wzUrl; + LPWSTR wzValue; + DWORD64 dw64Length; + + ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttributes; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_CONTENT +{ + LPWSTR wzType; + LPWSTR wzUrl; + LPWSTR wzValue; + + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_AUTHOR +{ + LPWSTR wzName; + LPWSTR wzEmail; + LPWSTR wzUrl; +}; + +struct ATOM_CATEGORY +{ + LPWSTR wzLabel; + LPWSTR wzScheme; + LPWSTR wzTerm; + + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_ENTRY +{ + LPWSTR wzId; + LPWSTR wzSummary; + LPWSTR wzTitle; + FILETIME ftPublished; + FILETIME ftUpdated; + + ATOM_CONTENT* pContent; + + DWORD cAuthors; + ATOM_AUTHOR* rgAuthors; + + DWORD cCategories; + ATOM_CATEGORY* rgCategories; + + DWORD cLinks; + ATOM_LINK* rgLinks; + + IXMLDOMNode* pixn; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_FEED +{ + LPWSTR wzGenerator; + LPWSTR wzIcon; + LPWSTR wzId; + LPWSTR wzLogo; + LPWSTR wzSubtitle; + LPWSTR wzTitle; + FILETIME ftUpdated; + + DWORD cAuthors; + ATOM_AUTHOR* rgAuthors; + + DWORD cCategories; + ATOM_CATEGORY* rgCategories; + + DWORD cEntries; + ATOM_ENTRY* rgEntries; + + DWORD cLinks; + ATOM_LINK* rgLinks; + + IXMLDOMNode* pixn; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +HRESULT DAPI AtomInitialize( + ); + +void DAPI AtomUninitialize( + ); + +HRESULT DAPI AtomParseFromString( + __in_z LPCWSTR wzAtomString, + __out ATOM_FEED **ppFeed + ); + +HRESULT DAPI AtomParseFromFile( + __in_z LPCWSTR wzAtomFile, + __out ATOM_FEED **ppFeed + ); + +HRESULT DAPI AtomParseFromDocument( + __in IXMLDOMDocument* pixdDocument, + __out ATOM_FEED **ppFeed + ); + +void DAPI AtomFreeFeed( + __in_xcount(pFeed->cItems) ATOM_FEED *pFEED + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h new file mode 100644 index 00000000..e61cdb58 --- /dev/null +++ b/src/dutil/inc/buffutil.h @@ -0,0 +1,80 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +// macro definitions + +#define ReleaseBuffer ReleaseMem +#define ReleaseNullBuffer ReleaseNullMem +#define BuffFree MemFree + + +// function declarations + +HRESULT BuffReadNumber( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD* pdw + ); +HRESULT BuffReadNumber64( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD64* pdw64 + ); +HRESULT BuffReadString( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPWSTR* pscz + ); +HRESULT BuffReadStringAnsi( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPSTR* pscz + ); +HRESULT BuffReadStream( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_bcount(*pcbStream) BYTE** ppbStream, + __out SIZE_T* pcbStream + ); + +HRESULT BuffWriteNumber( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD dw + ); +HRESULT BuffWriteNumber64( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD64 dw64 + ); +HRESULT BuffWriteString( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCWSTR scz + ); +HRESULT BuffWriteStringAnsi( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCSTR scz + ); +HRESULT BuffWriteStream( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_bcount(cbStream) const BYTE* pbStream, + __in SIZE_T cbStream + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/butil.h b/src/dutil/inc/butil.h new file mode 100644 index 00000000..a42cac11 --- /dev/null +++ b/src/dutil/inc/butil.h @@ -0,0 +1,31 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +enum BUNDLE_INSTALL_CONTEXT +{ + BUNDLE_INSTALL_CONTEXT_MACHINE, + BUNDLE_INSTALL_CONTEXT_USER, +}; + +HRESULT DAPI BundleGetBundleInfo( + __in LPCWSTR szBundleId, // Bundle code + __in LPCWSTR szAttribute, // attribute name + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired + __inout_opt LPDWORD pcchValueBuf // in/out buffer character count + ); + +HRESULT DAPI BundleEnumRelatedBundle( + __in LPCWSTR lpUpgradeCode, + __in BUNDLE_INSTALL_CONTEXT context, + __inout PDWORD pdwStartIndex, + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/cabcutil.h b/src/dutil/inc/cabcutil.h new file mode 100644 index 00000000..4f0c7b13 --- /dev/null +++ b/src/dutil/inc/cabcutil.h @@ -0,0 +1,62 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + +// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method +// First argument is the name of splitting cabinet without extension e.g. "cab1" +// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" +// Third argument is the file token of the first file present in the splitting cabinet +typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR); + +#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866) + +#ifdef __cplusplus +extern "C" { +#endif + +extern const int CABC_HANDLE_BYTES; + +// time vs. space trade-off +typedef enum COMPRESSION_TYPE +{ + COMPRESSION_TYPE_NONE, // fastest + COMPRESSION_TYPE_LOW, + COMPRESSION_TYPE_MEDIUM, + COMPRESSION_TYPE_HIGH, // smallest + COMPRESSION_TYPE_MSZIP +} COMPRESSION_TYPE; + +// functions +HRESULT DAPI CabCBegin( + __in_z LPCWSTR wzCab, + __in_z LPCWSTR wzCabDir, + __in DWORD dwMaxFiles, + __in DWORD dwMaxSize, + __in DWORD dwMaxThresh, + __in COMPRESSION_TYPE ct, + __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext + ); +HRESULT DAPI CabCNextCab( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); +HRESULT DAPI CabCAddFile( + __in_z LPCWSTR wzFile, + __in_z_opt LPCWSTR wzToken, + __in_opt PMSIFILEHASHINFO pmfHash, + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); +HRESULT DAPI CabCFinish( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback + ); +void DAPI CabCCancel( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/cabutil.h b/src/dutil/inc/cabutil.h new file mode 100644 index 00000000..0bedba80 --- /dev/null +++ b/src/dutil/inc/cabutil.h @@ -0,0 +1,56 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + + +// callback function prototypes +typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile); +typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); +typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); +typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod); +typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile); + +typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile); +typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile); +typedef HRESULT (*CAB_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); + +// function type with calling convention of __stdcall that .NET 1.1 understands only +// .NET 2.0 will not need this +typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin); + + +// functions +HRESULT DAPI CabInitialize( + __in BOOL fDelayLoad + ); +void DAPI CabUninitialize( + ); + +HRESULT DAPI CabExtract( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzExtractFile, + __in_z LPCWSTR wzExtractDir, + __in_opt CAB_CALLBACK_PROGRESS pfnProgress, + __in_opt LPVOID pvContext, + __in DWORD64 dw64EmbeddedOffset + ); + +HRESULT DAPI CabEnumerate( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzEnumerateFile, + __in STDCALL_PFNFDINOTIFY pfnNotify, + __in DWORD64 dw64EmbeddedOffset + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/certutil.h b/src/dutil/inc/certutil.h new file mode 100644 index 00000000..8565c1cf --- /dev/null +++ b/src/dutil/inc/certutil.h @@ -0,0 +1,66 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ReleaseCertStore(p) if (p) { ::CertCloseStore(p, 0); p = NULL; } +#define ReleaseCertContext(p) if (p) { ::CertFreeCertificateContext(p); p = NULL; } +#define ReleaseCertChain(p) if (p) { ::CertFreeCertificateChain(p); p = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI CertReadProperty( + __in PCCERT_CONTEXT pCertContext, + __in DWORD dwProperty, + __out_bcount(*pcbValue) LPVOID pvValue, + __out_opt DWORD* pcbValue + ); + +HRESULT DAPI CertGetAuthenticodeSigningTimestamp( + __in CMSG_SIGNER_INFO* pSignerInfo, + __out FILETIME* pft + ); + +HRESULT DAPI GetCryptProvFromCert( + __in_opt HWND hwnd, + __in PCCERT_CONTEXT pCert, + __out HCRYPTPROV *phCryptProv, + __out DWORD *pdwKeySpec, + __in BOOL *pfDidCryptAcquire, + __deref_opt_out LPWSTR *ppwszTmpContainer, + __deref_opt_out LPWSTR *ppwszProviderName, + __out DWORD *pdwProviderType + ); + +HRESULT DAPI FreeCryptProvFromCert( + __in BOOL fAcquired, + __in HCRYPTPROV hProv, + __in_opt LPWSTR pwszCapiProvider, + __in DWORD dwProviderType, + __in_opt LPWSTR pwszTmpContainer + ); + +HRESULT DAPI GetProvSecurityDesc( + __in HCRYPTPROV hProv, + __deref_out SECURITY_DESCRIPTOR** pSecurity + ); + +HRESULT DAPI SetProvSecurityDesc( + __in HCRYPTPROV hProv, + __in SECURITY_DESCRIPTOR* pSecurity + ); + +BOOL DAPI CertHasPrivateKey( + __in PCCERT_CONTEXT pCertContext, + __out_opt DWORD* pdwKeySpec + ); + +HRESULT DAPI CertInstallSingleCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzName + ); +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/condutil.h b/src/dutil/inc/condutil.h new file mode 100644 index 00000000..fb960042 --- /dev/null +++ b/src/dutil/inc/condutil.h @@ -0,0 +1,19 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// function declarations + +HRESULT DAPI CondEvaluate( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/dutil/inc/conutil.h b/src/dutil/inc/conutil.h new file mode 100644 index 00000000..cfb65332 --- /dev/null +++ b/src/dutil/inc/conutil.h @@ -0,0 +1,78 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ConsoleExitOnFailure(x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } +#define ConsoleExitOnLastError(x, c, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } } +#define ConsoleExitOnNull(p, x, e, c, f, ...) if (NULL == p) { x = e; ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } + + +// the following macros need to go away +#define ConsoleTrace(l, f, ...) { ConsoleWriteLine(CONSOLE_COLOR_NORMAL, f, __VA_ARGS__); Trace(l, f, __VA_ARGS__); } +#define ConsoleWarning(f, ...) { ConsoleWriteLine(CONSOLE_COLOR_YELLOW, f, __VA_ARGS__); Trace(REPORT_STANDARD, f, __VA_ARGS__); } +#define ConsoleError(x, f, ...) { ConsoleWriteError(x, CONSOLE_COLOR_RED, f, __VA_ARGS__); TraceError(x, f, __VA_ARGS__); } + + +// enums +typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR; + +// structs + +// functions +HRESULT DAPI ConsoleInitialize(); +void DAPI ConsoleUninitialize(); + +void DAPI ConsoleGreen(); +void DAPI ConsoleRed(); +void DAPI ConsoleYellow(); +void DAPI ConsoleNormal(); + +HRESULT DAPI ConsoleWrite( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI ConsoleWriteLine( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI ConsoleWriteError( + HRESULT hrError, + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI ConsoleReadW( + __deref_out_z LPWSTR* ppwzBuffer + ); + +HRESULT DAPI ConsoleReadStringA( + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ); +HRESULT DAPI ConsoleReadStringW( + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ); + +HRESULT DAPI ConsoleReadNonBlockingW( + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, + __out DWORD* pcchSize, + BOOL fReadLine + ); + +HRESULT DAPI ConsoleSetReadHidden(void); +HRESULT DAPI ConsoleSetReadNormal(void); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/cryputil.h b/src/dutil/inc/cryputil.h new file mode 100644 index 00000000..88aa784d --- /dev/null +++ b/src/dutil/inc/cryputil.h @@ -0,0 +1,103 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ReleaseCryptMsg(p) if (p) { ::CryptMsgClose(p); p = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + + +// Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE. +#define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE +#define SHA1_HASH_LEN 20 + +typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)( + __inout PVOID Memory, + __in ULONG MemoryLength, + __in ULONG OptionFlags + ); + +typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)( + __inout PVOID Memory, + __in ULONG MemoryLength, + __in ULONG OptionFlags + ); + +typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +// function declarations + +HRESULT DAPI CrypInitialize(); +void DAPI CrypUninitialize(); + +HRESULT DAPI CrypDecodeObject( + __in_z LPCSTR szStructType, + __in_ecount(cbData) const BYTE* pbData, + __in DWORD cbData, + __in DWORD dwFlags, + __out LPVOID* ppvObject, + __out_opt DWORD* pcbObject + ); + +HRESULT DAPI CrypMsgGetParam( + __in HCRYPTMSG hCryptMsg, + __in DWORD dwType, + __in DWORD dwIndex, + __out LPVOID* ppvData, + __out_opt DWORD* pcbData + ); + +HRESULT DAPI CrypHashFile( + __in_z LPCWSTR wzFilePath, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ); + +HRESULT DAPI CrypHashFileHandle( + __in HANDLE hFile, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ); + +HRESULT DAPI CrypHashBuffer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash + ); + +HRESULT DAPI CrypEncryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +HRESULT DAPI CrypDecryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/dictutil.h b/src/dutil/inc/dictutil.h new file mode 100644 index 00000000..f0a3bb5a --- /dev/null +++ b/src/dutil/inc/dictutil.h @@ -0,0 +1,69 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); } +#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; } + +typedef void* STRINGDICT_HANDLE; +typedef const void* C_STRINGDICT_HANDLE; + +extern const int STRINGDICT_HANDLE_BYTES; + +typedef enum DICT_FLAG +{ + DICT_FLAG_NONE = 0, + DICT_FLAG_CASEINSENSITIVE = 1 +} DICT_FLAG; + +HRESULT DAPI DictCreateWithEmbeddedKey( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in_opt void **ppvArray, + __in size_t cByteOffset, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCreateStringList( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCreateStringListFromArray( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCompareStringListToArray( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray + ); +HRESULT DAPI DictAddKey( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString + ); +HRESULT DAPI DictAddValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in void *pvValue + ); +HRESULT DAPI DictKeyExists( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString + ); +HRESULT DAPI DictGetValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString, + __out void **ppvValue + ); +void DAPI DictDestroy( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/dirutil.h b/src/dutil/inc/dirutil.h new file mode 100644 index 00000000..0a19a9c0 --- /dev/null +++ b/src/dutil/inc/dirutil.h @@ -0,0 +1,54 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +typedef enum DIR_DELETE +{ + DIR_DELETE_FILES = 1, + DIR_DELETE_RECURSE = 2, + DIR_DELETE_SCHEDULE = 4, +} DIR_DELETE; + +#ifdef __cplusplus +extern "C" { +#endif + +BOOL DAPI DirExists( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); + +HRESULT DAPI DirCreateTempPath( + __in_z LPCWSTR wzPrefix, + __out_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD cchPath + ); + +HRESULT DAPI DirEnsureExists( + __in_z LPCWSTR wzPath, + __in_opt LPSECURITY_ATTRIBUTES psa + ); + +HRESULT DAPI DirEnsureDelete( + __in_z LPCWSTR wzPath, + __in BOOL fDeleteFiles, + __in BOOL fRecurse + ); + +HRESULT DAPI DirEnsureDeleteEx( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ); + +HRESULT DAPI DirGetCurrent( + __deref_out_z LPWSTR* psczCurrentDirectory + ); + +HRESULT DAPI DirSetCurrent( + __in_z LPCWSTR wzDirectory + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/dlutil.h b/src/dutil/inc/dlutil.h new file mode 100644 index 00000000..3e95103a --- /dev/null +++ b/src/dutil/inc/dlutil.h @@ -0,0 +1,59 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef HRESULT (WINAPI *LPAUTHENTICATION_ROUTINE)( + __in LPVOID pVoid, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); + +typedef int (WINAPI *LPCANCEL_ROUTINE)( + __in HRESULT hrError, + __in_z_opt LPCWSTR wzError, + __in BOOL fAllowRetry, + __in_opt LPVOID pvContext + ); + +// structs +typedef struct _DOWNLOAD_SOURCE +{ + LPWSTR sczUrl; + LPWSTR sczUser; + LPWSTR sczPassword; +} DOWNLOAD_SOURCE; + +typedef struct _DOWNLOAD_CACHE_CALLBACK +{ + LPPROGRESS_ROUTINE pfnProgress; + LPCANCEL_ROUTINE pfnCancel; + LPVOID pv; +} DOWNLOAD_CACHE_CALLBACK; + +typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK +{ + LPAUTHENTICATION_ROUTINE pfnAuthenticate; + LPVOID pv; +} DOWNLOAD_AUTHENTICATION_CALLBACK; + + +// functions + +HRESULT DAPI DownloadUrl( + __in DOWNLOAD_SOURCE* pDownloadSource, + __in DWORD64 dw64AuthoredDownloadSize, + __in LPCWSTR wzDestinationPath, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ); + + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h new file mode 100644 index 00000000..3791dd5a --- /dev/null +++ b/src/dutil/inc/dutil.h @@ -0,0 +1,160 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define DAPI __stdcall +#define DAPIV __cdecl // used only for functions taking variable length arguments + +#define DAPI_(type) EXTERN_C type DAPI +#define DAPIV_(type) EXTERN_C type DAPIV + + +// enums +typedef enum REPORT_LEVEL +{ + REPORT_NONE, // turns off report (only valid for XXXSetLevel()) + REPORT_WARNING, // written if want only warnings or reporting is on in general + REPORT_STANDARD, // written if reporting is on + REPORT_VERBOSE, // written only if verbose reporting is on + REPORT_DEBUG, // reporting useful when debugging code + REPORT_ERROR, // always gets reported, but can never be specified +} REPORT_LEVEL; + +// asserts and traces +typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); + +#ifdef __cplusplus +extern "C" { +#endif + +void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule); +void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn); +void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine); +void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z LPCSTR szMessage); + +void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames); +REPORT_LEVEL DAPI Dutil_TraceGetLevel(); +void __cdecl Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...); +void __cdecl Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); +void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError); + +#ifdef __cplusplus +} +#endif + + +#ifdef DEBUG + +#define AssertSetModule(m) (void)Dutil_SetAssertModule(m) +#define AssertSetDisplayFunction(pfn) (void)Dutil_SetAssertDisplayFunction(pfn) +#define Assert(f) ((f) ? (void)0 : (void)Dutil_Assert(__FILE__, __LINE__)) +#define AssertSz(f, sz) ((f) ? (void)0 : (void)Dutil_AssertSz(__FILE__, __LINE__, sz)) + +#define TraceSetLevel(l, f) (void)Dutil_TraceSetLevel(l, f) +#define TraceGetLevel() (REPORT_LEVEL)Dutil_TraceGetLevel() +#define Trace(l, f, ...) (void)Dutil_Trace(__FILE__, __LINE__, l, f, __VA_ARGS__) +#define TraceError(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_ERROR, x, f, __VA_ARGS__) +#define TraceErrorDebug(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_DEBUG, x, f, __VA_ARGS__) + +#else // !DEBUG + +#define AssertSetModule(m) +#define AssertSetDisplayFunction(pfn) +#define Assert(f) +#define AssertSz(f, sz) + +#define TraceSetLevel(l, f) +#define Trace(l, f, ...) +#define TraceError(x, f, ...) +#define TraceErrorDebug(x, f, ...) + +#endif // DEBUG + +// ExitTrace can be overriden +#ifndef ExitTrace +#define ExitTrace TraceError +#endif + +// Exit macros +#define ExitFunction() { goto LExit; } +#define ExitFunction1(x) { x; goto LExit; } + +#define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; } + +#define ExitOnLastError(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } } +#define ExitOnLastErrorDebugTrace(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; } } +#define ExitWithLastError(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnFailure(x, s, ...) if (FAILED(x)) { ExitTrace(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnRootFailure(x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnFailureDebugTrace(x, s, ...) if (FAILED(x)) { TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnNull(p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnNullWithLastError(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); ExitTrace(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnNullDebugTrace(p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnInvalidHandleWithLastError(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); ExitTrace(x, s, __VA_ARGS__); goto LExit; } +#define ExitOnWin32Error(e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } + +// release macros +#define ReleaseObject(x) if (x) { x->Release(); } +#define ReleaseObjectArray(prg, cel) if (prg) { for (DWORD Dutil_ReleaseObjectArrayIndex = 0; Dutil_ReleaseObjectArrayIndex < cel; ++Dutil_ReleaseObjectArrayIndex) { ReleaseObject(prg[Dutil_ReleaseObjectArrayIndex]); } ReleaseMem(prg); } +#define ReleaseVariant(x) { ::VariantClear(&x); } +#define ReleaseNullObject(x) if (x) { (x)->Release(); x = NULL; } +#define ReleaseCertificate(x) if (x) { ::CertFreeCertificateContext(x); x=NULL; } +#define ReleaseHandle(x) if (x) { ::CloseHandle(x); x = NULL; } + + +// useful defines and macros +#define Unused(x) ((void)x) + +#ifndef countof +#if 1 +#define countof(ary) (sizeof(ary) / sizeof(ary[0])) +#else +#ifndef __cplusplus +#define countof(ary) (sizeof(ary) / sizeof(ary[0])) +#else +template static char countofVerify(void const *, T) throw() { return 0; } +template static void countofVerify(T *const, T *const *) throw() {}; +#define countof(arr) (sizeof(countofVerify(arr,&(arr))) * sizeof(arr)/sizeof(*(arr))) +#endif +#endif +#endif + +#define roundup(x, n) roundup_typed(x, n, DWORD) +#define roundup_typed(x, n, t) (((t)(x) + ((t)(n) - 1)) & ~((t)(n) - 1)) + +#define HRESULT_FROM_RPC(x) ((HRESULT) ((x) | FACILITY_RPC)) + +#ifndef MAXSIZE_T +#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0)) +#endif + +typedef const BYTE* LPCBYTE; + +#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) +#define E_PATHNOTFOUND HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) +#define E_INVALIDDATA HRESULT_FROM_WIN32(ERROR_INVALID_DATA) +#define E_INVALIDSTATE HRESULT_FROM_WIN32(ERROR_INVALID_STATE) +#define E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) +#define E_MOREDATA HRESULT_FROM_WIN32(ERROR_MORE_DATA) +#define E_NOMOREITEMS HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) +#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) +#define E_MODNOTFOUND HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) +#define E_BADCONFIGURATION HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION) + +#define AddRefAndRelease(x) { x->AddRef(); x->Release(); } + +#define MAKEDWORD(lo, hi) ((DWORD)MAKELONG(lo, hi)) +#define MAKEQWORDVERSION(mj, mi, b, r) (((DWORD64)MAKELONG(r, b)) | (((DWORD64)MAKELONG(mi, mj)) << 32)) + + +#ifdef __cplusplus +extern "C" { +#endif + +// other functions +HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule); +HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/eseutil.h b/src/dutil/inc/eseutil.h new file mode 100644 index 00000000..1c408927 --- /dev/null +++ b/src/dutil/inc/eseutil.h @@ -0,0 +1,223 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); } +#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; } + +struct ESE_COLUMN_SCHEMA +{ + JET_COLUMNID jcColumn; + LPCWSTR pszName; + JET_COLTYP jcColumnType; + BOOL fKey; // If this column is part of the key of the table + BOOL fFixed; + BOOL fNullable; + BOOL fAutoIncrement; +}; + +struct ESE_TABLE_SCHEMA +{ + JET_TABLEID jtTable; + LPCWSTR pszName; + DWORD dwColumns; + ESE_COLUMN_SCHEMA *pcsColumns; +}; + +struct ESE_DATABASE_SCHEMA +{ + DWORD dwTables; + ESE_TABLE_SCHEMA *ptsTables; +}; + +typedef enum ESE_QUERY_TYPE +{ + ESE_QUERY_EXACT, + ESE_QUERY_FROM_TOP, + ESE_QUERY_FROM_BOTTOM +} ESE_QUERY_TYPE; + +typedef void* ESE_QUERY_HANDLE; + +HRESULT DAPI EseBeginSession( + __out JET_INSTANCE *pjiInstance, + __out JET_SESID *pjsSession, + __in_z LPCWSTR pszInstance, + __in_z LPCWSTR pszPath + ); +HRESULT DAPI EseEndSession( + __in JET_INSTANCE jiInstance, + __in JET_SESID jsSession + ); +HRESULT DAPI EseEnsureDatabase( + __in JET_SESID jsSession, + __in_z LPCWSTR pszFile, + __in ESE_DATABASE_SCHEMA *pdsSchema, + __out JET_DBID* pjdbDb, + __in BOOL fExclusive, + __in BOOL fReadonly + ); +HRESULT DAPI EseCloseDatabase( + __in JET_SESID jsSession, + __in JET_DBID jdbDb + ); +HRESULT DAPI EseCreateTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ); +HRESULT DAPI EseOpenTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ); +HRESULT DAPI EseCloseTable( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ); +HRESULT DAPI EseEnsureColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __in JET_COLTYP jcColumnType, + __in ULONG ulColumnSize, + __in BOOL fFixed, + __in BOOL fNullable, + __out_opt JET_COLUMNID *pjcColumn + ); +HRESULT DAPI EseGetColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __out JET_COLUMNID *pjcColumn + ); +HRESULT DAPI EseMoveCursor( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in LONG lRow + ); +HRESULT DAPI EseDeleteRow( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ); +HRESULT DAPI EseBeginTransaction( + __in JET_SESID jsSession + ); +HRESULT DAPI EseRollbackTransaction( + __in JET_SESID jsSession, + __in BOOL fAll + ); +HRESULT DAPI EseCommitTransaction( + __in JET_SESID jsSession + ); +HRESULT DAPI EsePrepareUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ULONG ulPrep + ); +HRESULT DAPI EseFinishUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in BOOL fSeekToInsertedRecord + ); +HRESULT DAPI EseSetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI EseSetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in DWORD dwValue + ); +HRESULT DAPI EseSetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in BOOL fValue + ); +HRESULT DAPI EseSetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_z LPCWSTR pszValue + ); +HRESULT DAPI EseSetColumnEmpty( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn + ); +HRESULT DAPI EseGetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT DAPI EseGetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out DWORD *pdwValue + ); +HRESULT DAPI EseGetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out BOOL *pfValue + ); +HRESULT DAPI EseGetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out LPWSTR *ppszValue + ); + +// Call this once for each key column in the table +HRESULT DAPI EseBeginQuery( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ESE_QUERY_TYPE qtQueryType, + __out ESE_QUERY_HANDLE *peqhHandle + ); +HRESULT DAPI EseSetQueryColumnBinary( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnDword( + __in ESE_QUERY_HANDLE eqhHandle, + __in DWORD dwData, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnBool( + __in ESE_QUERY_HANDLE eqhHandle, + __in BOOL fValue, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnString( + __in ESE_QUERY_HANDLE eqhHandle, + __in_z LPCWSTR pszString, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseFinishQuery( + __in ESE_QUERY_HANDLE eqhHandle + ); +// Once all columns have been set up, call this and read the result +HRESULT DAPI EseRunQuery( + __in ESE_QUERY_HANDLE eqhHandle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h new file mode 100644 index 00000000..ddae340f --- /dev/null +++ b/src/dutil/inc/fileutil.h @@ -0,0 +1,235 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseFile(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } +#define ReleaseFileHandle(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } +#define ReleaseFileFindHandle(h) if (INVALID_HANDLE_VALUE != h) { ::FindClose(h); h = INVALID_HANDLE_VALUE; } + +#define FILEMAKEVERSION(major, minor, build, revision) static_cast((static_cast(major & 0xFFFF) << 48) \ + | (static_cast(minor & 0xFFFF) << 32) \ + | (static_cast(build & 0xFFFF) << 16) \ + | (static_cast(revision & 0xFFFF))) + +typedef enum FILE_ARCHITECTURE +{ + FILE_ARCHITECTURE_UNKNOWN, + FILE_ARCHITECTURE_X86, + FILE_ARCHITECTURE_X64, + FILE_ARCHITECTURE_IA64, +} FILE_ARCHITECTURE; + +typedef enum FILE_ENCODING +{ + FILE_ENCODING_UNSPECIFIED = 0, + // TODO: distinguish between non-BOM utf-8 and ANSI in the future? + FILE_ENCODING_UTF8, + FILE_ENCODING_UTF8_WITH_BOM, + FILE_ENCODING_UTF16, + FILE_ENCODING_UTF16_WITH_BOM, +} FILE_ENCODING; + + +LPWSTR DAPI FileFromPath( + __in_z LPCWSTR wzPath + ); +HRESULT DAPI FileResolvePath( + __in_z LPCWSTR wzRelativePath, + __out LPWSTR *ppwzFullPath + ); +HRESULT DAPI FileStripExtension( + __in_z LPCWSTR wzFileName, + __out LPWSTR *ppwzFileNameNoExtension + ); +HRESULT DAPI FileChangeExtension( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzNewExtension, + __out LPWSTR *ppwzFileNameNewExtension + ); +HRESULT DAPI FileAddSuffixToBaseName( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzSuffix, + __out_z LPWSTR* psczNewFileName + ); +HRESULT DAPI FileVersionFromString( + __in_z LPCWSTR wzVersion, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ); +HRESULT DAPI FileVersionFromStringEx( + __in_z LPCWSTR wzVersion, + __in DWORD cchVersion, + __out DWORD64* pqwVersion + ); +HRESULT DAPI FileVersionToStringEx( + __in DWORD64 qwVersion, + __out LPWSTR* psczVersion + ); +HRESULT DAPI FileSetPointer( + __in HANDLE hFile, + __in DWORD64 dw64Move, + __out_opt DWORD64* pdw64NewPosition, + __in DWORD dwMoveMethod + ); +HRESULT DAPI FileSize( + __in_z LPCWSTR pwzFileName, + __out LONGLONG* pllSize + ); +HRESULT DAPI FileSizeByHandle( + __in HANDLE hFile, + __out LONGLONG* pllSize + ); +BOOL DAPI FileExistsEx( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); +BOOL DAPI FileExistsAfterRestart( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); +HRESULT DAPI FileRemoveFromPendingRename( + __in_z LPCWSTR wzPath + ); +HRESULT DAPI FileRead( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out DWORD* pcbDest, + __in_z LPCWSTR wzSrcPath + ); +HRESULT DAPI FileReadEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out DWORD* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD dwShareMode + ); +HRESULT DAPI FileReadUntil( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) DWORD* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD cbMaxRead + ); +HRESULT DAPI FileReadPartial( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) DWORD* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK + ); +HRESULT DAPI FileReadPartialEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) DWORD* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK, + __in DWORD dwShareMode + ); +HRESULT DAPI FileWrite( + __in_z LPCWSTR pwzFileName, + __in DWORD dwFlagsAndAttributes, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in DWORD cbData, + __out_opt HANDLE* pHandle + ); +HRESULT DAPI FileWriteHandle( + __in HANDLE hFile, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in DWORD cbData + ); +HRESULT DAPI FileCopyUsingHandles( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __out_opt DWORD64* pcbCopied + ); +HRESULT DAPI FileEnsureCopy( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite + ); +HRESULT DAPI FileEnsureCopyWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); +HRESULT DAPI FileEnsureMove( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy + ); +HRESULT DAPI FileEnsureMoveWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); +HRESULT DAPI FileCreateTemp( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ); +HRESULT DAPI FileCreateTempW( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ); +HRESULT DAPI FileVersion( + __in_z LPCWSTR wzFilename, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ); +HRESULT DAPI FileIsSame( + __in_z LPCWSTR wzFile1, + __in_z LPCWSTR wzFile2, + __out LPBOOL lpfSameFile + ); +HRESULT DAPI FileEnsureDelete( + __in_z LPCWSTR wzFile + ); +HRESULT DAPI FileGetTime( + __in_z LPCWSTR wzFile, + __out_opt LPFILETIME lpCreationTime, + __out_opt LPFILETIME lpLastAccessTime, + __out_opt LPFILETIME lpLastWriteTime + ); +HRESULT DAPI FileSetTime( + __in_z LPCWSTR wzFile, + __in_opt const FILETIME *lpCreationTime, + __in_opt const FILETIME *lpLastAccessTime, + __in_opt const FILETIME *lpLastWriteTime + ); +HRESULT DAPI FileResetTime( + __in_z LPCWSTR wzFile + ); +HRESULT DAPI FileExecutableArchitecture( + __in_z LPCWSTR wzFile, + __out FILE_ARCHITECTURE *pArchitecture + ); +HRESULT DAPI FileToString( + __in_z LPCWSTR wzFile, + __out LPWSTR *psczString, + __out_opt FILE_ENCODING *pfeEncoding + ); +HRESULT DAPI FileFromString( + __in_z LPCWSTR wzFile, + __in DWORD dwFlagsAndAttributes, + __in_z LPCWSTR sczString, + __in FILE_ENCODING feEncoding + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/gdiputil.h b/src/dutil/inc/gdiputil.h new file mode 100644 index 00000000..3708053c --- /dev/null +++ b/src/dutil/inc/gdiputil.h @@ -0,0 +1,38 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ExitOnGdipFailure(g, x, s, ...) { x = GdipHresultFromStatus(g); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } } + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI GdipInitialize( + __in const Gdiplus::GdiplusStartupInput* pInput, + __out ULONG_PTR* pToken, + __out_opt Gdiplus::GdiplusStartupOutput *pOutput + ); + +void DAPI GdipUninitialize( + __in ULONG_PTR token + ); + +HRESULT DAPI GdipBitmapFromResource( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szId, + __out Gdiplus::Bitmap **ppBitmap + ); + +HRESULT DAPI GdipBitmapFromFile( + __in_z LPCWSTR wzFileName, + __out Gdiplus::Bitmap **ppBitmap + ); + +HRESULT DAPI GdipHresultFromStatus( + __in Gdiplus::Status gs + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/guidutil.h b/src/dutil/inc/guidutil.h new file mode 100644 index 00000000..478a464f --- /dev/null +++ b/src/dutil/inc/guidutil.h @@ -0,0 +1,21 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUID_STRING_LENGTH 39 + +HRESULT DAPI GuidFixedCreate( + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid + ); + +HRESULT DAPI GuidCreate( + __deref_out_z LPWSTR* psczGuid + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/iis7util.h b/src/dutil/inc/iis7util.h new file mode 100644 index 00000000..3572b4e9 --- /dev/null +++ b/src/dutil/inc/iis7util.h @@ -0,0 +1,222 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// IIS Config schema names +#define IIS_CONFIG_ADD L"add" +#define IIS_CONFIG_ALLOWED L"allowed" +#define IIS_CONFIG_APPHOST_ROOT L"MACHINE/WEBROOT/APPHOST" +#define IIS_CONFIG_APPLICATION L"application" +#define IIS_CONFIG_APPPOOL L"applicationPool" +#define IIS_CONFIG_APPPOOL_AUTO L"autoStart" +#define IIS_CONFIG_APPPOOL_SECTION L"system.applicationHost/applicationPools" +#define IIS_CONFIG_AUTOSTART L"serverAutoStart" +#define IIS_CONFIG_BINDING L"binding" +#define IIS_CONFIG_BINDINGINFO L"bindingInformation" +#define IIS_CONFIG_BINDINGS L"bindings" +#define IIS_CONFIG_DESC L"description" +#define IIS_CONFIG_EXECUTABLE L"scriptProcessor" +#define IIS_CONFIG_ENABLED L"enabled" +#define IIS_CONFIG_ENABLE32 L"enable32BitAppOnWin64" +#define IIS_CONFIG_FILEEXT L"fileExtension" +#define IIS_CONFIG_FILTER L"filter" +#define IIS_CONFIG_GROUPID L"groupId" +#define IIS_CONFIG_HEADERS L"customHeaders" +#define IIS_CONFIG_HTTPERRORS_SECTION L"system.webServer/httpErrors" +#define IIS_CONFIG_ID L"id" +#define IIS_CONFIG_ISAPI_SECTION L"system.webServer/isapiFilters" +#define IIS_CONFIG_HTTPPROTO_SECTION L"system.webServer/httpProtocol" +#define IIS_CONFIG_LOG_SECTION L"system.applicationHost/log" +#define IIS_CONFIG_LOG_UTF8 L"logInUTF8" +#define IIS_CONFIG_LIMITS L"limits" +#define IIS_CONFIG_PIPELINEMODE L"managedPipelineMode" +#define IIS_CONFIG_MANAGEDRUNTIMEVERSION L"managedRuntimeVersion" +#define IIS_CONFIG_WEBLOG L"logFile" +#define IIS_CONFIG_LOGFORMAT L"logFormat" +#define IIS_CONFIG_MIMEMAP L"mimeMap" +#define IIS_CONFIG_MIMETYPE L"mimeType" +#define IIS_CONFIG_MODULES L"modules" +#define IIS_CONFIG_NAME L"name" +#define IIS_CONFIG_PATH L"path" +#define IIS_CONFIG_PHYSPATH L"physicalPath" +#define IIS_CONFIG_PROTOCOL L"protocol" +#define IIS_CONFIG_RESTRICTION_SECTION L"system.webServer/security/isapiCgiRestriction" +#define IIS_CONFIG_SITE L"site" +#define IIS_CONFIG_SITE_ID L"id" +#define IIS_CONFIG_SITES_SECTION L"system.applicationHost/sites" +#define IIS_CONFIG_CONNECTTIMEOUT L"connectionTimeout" +#define IIS_CONFIG_VDIR L"virtualDirectory" +#define IIS_CONFIG_VALUE L"value" +#define IIS_CONFIG_VERBS L"verb" +#define IIS_CONFIG_WEBLIMITS_SECTION L"system.applicationHost/webLimits" +#define IIS_CONFIG_WEBLIMITS_MAXBAND L"maxGlobalBandwidth" +#define IIS_CONFIG_TRUE L"true" +#define IIS_CONFIG_FALSE L"false" +#define IIS_CONFIG_ERROR L"error" +#define IIS_CONFIG_STATUSCODE L"statusCode" +#define IIS_CONFIG_SUBSTATUS L"subStatusCode" +#define IIS_CONFIG_LANGPATH L"prefixLanguageFilePath" +#define IIS_CONFIG_RESPMODE L"responseMode" +#define IIS_CONFIG_CLEAR L"clear" +#define IIS_CONFIG_RECYCLING L"recycling" +#define IIS_CONFIG_PEROIDRESTART L"periodicRestart" +#define IIS_CONFIG_TIME L"time" +#define IIS_CONFIG_REQUESTS L"requests" +#define IIS_CONFIG_SCHEDULE L"schedule" +#define IIS_CONFIG_MEMORY L"memory" +#define IIS_CONFIG_PRIVMEMORY L"privateMemory" +#define IIS_CONFIG_PROCESSMODEL L"processModel" +#define IIS_CONFIG_IDLETIMEOUT L"idleTimeout" +#define IIS_CONFIG_QUEUELENGTH L"queueLength" +#define IIS_CONFIG_IDENITITYTYPE L"identityType" +#define IIS_CONFIG_LOCALSYSTEM L"LocalSystem" +#define IIS_CONFIG_LOCALSERVICE L"LocalService" +#define IIS_CONFIG_NETWORKSERVICE L"NetworkService" +#define IIS_CONFIG_SPECIFICUSER L"SpecificUser" +#define IIS_CONFIG_APPLICATIONPOOLIDENTITY L"ApplicationPoolIdentity" +#define IIS_CONFIG_USERNAME L"userName" +#define IIS_CONFIG_PASSWORD L"password" +#define IIS_CONFIG_CPU L"cpu" +#define IIS_CONFIG_LIMIT L"limit" +#define IIS_CONFIG_CPU_ACTION L"action" +#define IIS_CONFIG_KILLW3WP L"KillW3wp" +#define IIS_CONFIG_NOACTION L"NoAction" +#define IIS_CONFIG_RESETINTERVAL L"resetInterval" +#define IIS_CONFIG_MAXWRKPROCESSES L"maxProcesses" +#define IIS_CONFIG_HANDLERS_SECTION L"system.webServer/handlers" +#define IIS_CONFIG_DEFAULTDOC_SECTION L"system.webServer/defaultDocument" +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" +#define IIS_CONFIG_SCRIPTERROR L"scriptErrorSentToBrowser" +#define IIS_CONFIG_STATICCONTENT_SECTION L"system.webServer/staticContent" +#define IIS_CONFIG_HTTPEXPIRES L"httpExpires" +#define IIS_CONFIG_MAXAGE L"cacheControlMaxAge" +#define IIS_CONFIG_CLIENTCACHE L"clientCache" +#define IIS_CONFIG_CACHECONTROLMODE L"cacheControlMode" +#define IIS_CONFIG_USEMAXAGE L"UseMaxAge" +#define IIS_CONFIG_USEEXPIRES L"UseExpires" +#define IIS_CONFIG_CACHECUST L"cacheControlCustom" +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" +#define IIS_CONFIG_SESSION L"session" +#define IIS_CONFIG_ALLOWSTATE L"allowSessionState" +#define IIS_CONFIG_TIMEOUT L"timeout" +#define IIS_CONFIG_BUFFERING L"bufferingOn" +#define IIS_CONFIG_PARENTPATHS L"enableParentPaths" +#define IIS_CONFIG_SCRIPTLANG L"scriptLanguage" +#define IIS_CONFIG_SCRIPTTIMEOUT L"scriptTimeout" +#define IIS_CONFIG_LIMITS L"limits" +#define IIS_CONFIG_ALLOWDEBUG L"appAllowDebugging" +#define IIS_CONFIG_ALLOWCLIENTDEBUG L"appAllowClientDebug" +#define IIS_CONFIG_CERTIFICATEHASH L"certificateHash" +#define IIS_CONFIG_CERTIFICATESTORENAME L"certificateStoreName" +#define IIS_CONFIG_HTTPLOGGING_SECTION L"system.webServer/httpLogging" +#define IIS_CONFIG_DONTLOG L"dontLog" + +typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID); +typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*); + +HRESULT DAPI Iis7PutPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT vtPut + ); + +HRESULT DAPI Iis7PutPropertyInteger( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in DWORD dValue + ); + +HRESULT DAPI Iis7PutPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPCWSTR wzString + ); + +HRESULT DAPI Iis7PutPropertyBool( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in BOOL fValue); + +HRESULT DAPI Iis7GetPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT* vtGet + ); + +HRESULT DAPI Iis7GetPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPWSTR* psczGet + ); + +struct IIS7_APPHOSTELEMENTCOMPARISON +{ + LPCWSTR sczElementName; + LPCWSTR sczAttributeName; + VARIANT* pvAttributeValue; + VARIANTCOMPARATORPROC pComparator; +}; + +BOOL DAPI Iis7IsMatchingAppHostElement( + __in IAppHostElement *pElement, + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison + ); + +HRESULT DAPI Iis7FindAppHostElementString( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementPath( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementInteger( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in DWORD dwAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementVariant( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in VARIANT* pvAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7EnumAppHostElements( + __in IAppHostElementCollection *pCollection, + __in ENUMAPHOSTELEMENTPROC pCallback, + __in LPVOID pContext, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostMethod( + __in IAppHostMethodCollection *pCollection, + __in LPCWSTR wzMethodName, + __out IAppHostMethod** ppMethod, + __out DWORD* pdwIndex + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/inetutil.h b/src/dutil/inc/inetutil.h new file mode 100644 index 00000000..4cbf510b --- /dev/null +++ b/src/dutil/inc/inetutil.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } +#define ReleaseNullInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } + + +// functions +HRESULT DAPI InternetGetSizeByHandle( + __in HINTERNET hiFile, + __out LONGLONG* pllSize + ); + +HRESULT DAPI InternetGetCreateTimeByHandle( + __in HINTERNET hiFile, + __out LPFILETIME pft + ); + +HRESULT DAPI InternetQueryInfoString( + __in HINTERNET h, + __in DWORD dwInfo, + __deref_out_z LPWSTR* psczValue + ); + +HRESULT DAPI InternetQueryInfoNumber( + __in HINTERNET h, + __in DWORD dwInfo, + __out LONG* plInfo + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/iniutil.h b/src/dutil/inc/iniutil.h new file mode 100644 index 00000000..d5b50c17 --- /dev/null +++ b/src/dutil/inc/iniutil.h @@ -0,0 +1,79 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); } +#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; } + +typedef void* INI_HANDLE; +typedef const void* C_INI_HANDLE; + +extern const int INI_HANDLE_BYTES; + +struct INI_VALUE +{ + LPCWSTR wzName; + LPCWSTR wzValue; + + DWORD dwLineNumber; +}; + +HRESULT DAPI IniInitialize( + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle + ); +void DAPI IniUninitialize( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle + ); +HRESULT DAPI IniSetOpenTag( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzOpenTagPrefix, + __in_z_opt LPCWSTR wzOpenTagPostfix + ); +HRESULT DAPI IniSetValueStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzValuePrefix, + __in_z_opt LPCWSTR wzValueSeparator + ); +HRESULT DAPI IniSetValueSeparatorException( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z LPCWSTR wzValueNamePrefix + ); +HRESULT DAPI IniSetCommentStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzLinePrefix + ); +HRESULT DAPI IniParse( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzPath, + __out_opt FILE_ENCODING *pfeEncodingFound + ); +// Gets the full value array, this includes values that may have been deleted +// (their value will be NULL) +HRESULT DAPI IniGetValueList( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __deref_out_ecount_opt(pcValues) INI_VALUE** prgivValues, + __out DWORD *pcValues + ); +HRESULT DAPI IniGetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __deref_out_z LPWSTR* psczValue + ); +HRESULT DAPI IniSetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI IniWriteFile( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzPath, + __in FILE_ENCODING feOverrideEncoding + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/jsonutil.h b/src/dutil/inc/jsonutil.h new file mode 100644 index 00000000..b05e9970 --- /dev/null +++ b/src/dutil/inc/jsonutil.h @@ -0,0 +1,112 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum JSON_TOKEN +{ + JSON_TOKEN_NONE, + JSON_TOKEN_ARRAY_START, + JSON_TOKEN_ARRAY_VALUE, + JSON_TOKEN_ARRAY_END, + JSON_TOKEN_OBJECT_START, + JSON_TOKEN_OBJECT_KEY, + JSON_TOKEN_OBJECT_VALUE, + JSON_TOKEN_OBJECT_END, + JSON_TOKEN_VALUE, +} JSON_TOKEN; + +typedef struct _JSON_VALUE +{ +} JSON_VALUE; + +typedef struct _JSON_READER +{ + CRITICAL_SECTION cs; + LPWSTR sczJson; + + LPWSTR pwz; + JSON_TOKEN token; +} JSON_READER; + +typedef struct _JSON_WRITER +{ + CRITICAL_SECTION cs; + LPWSTR sczJson; + + JSON_TOKEN* rgTokenStack; + DWORD cTokens; + DWORD cMaxTokens; +} JSON_WRITER; + + +DAPI_(HRESULT) JsonInitializeReader( + __in_z LPCWSTR wzJson, + __in JSON_READER* pReader + ); + +DAPI_(void) JsonUninitializeReader( + __in JSON_READER* pReader + ); + +DAPI_(HRESULT) JsonReadNext( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken, + __out JSON_VALUE* pValue + ); + +DAPI_(HRESULT) JsonReadValue( + __in JSON_READER* pReader, + __in JSON_VALUE* pValue + ); + +DAPI_(HRESULT) JsonInitializeWriter( + __in JSON_WRITER* pWriter + ); + +DAPI_(void) JsonUninitializeWriter( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteBool( + __in JSON_WRITER* pWriter, + __in BOOL fValue + ); + +DAPI_(HRESULT) JsonWriteNumber( + __in JSON_WRITER* pWriter, + __in DWORD dwValue + ); + +DAPI_(HRESULT) JsonWriteString( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzValue + ); + +DAPI_(HRESULT) JsonWriteArrayStart( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteArrayEnd( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteObjectStart( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteObjectKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ); + +DAPI_(HRESULT) JsonWriteObjectEnd( + __in JSON_WRITER* pWriter + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/locutil.h b/src/dutil/inc/locutil.h new file mode 100644 index 00000000..38ddda20 --- /dev/null +++ b/src/dutil/inc/locutil.h @@ -0,0 +1,120 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +struct LOC_STRING +{ + LPWSTR wzId; + LPWSTR wzText; + BOOL bOverridable; +}; + +const int LOC_CONTROL_NOT_SET = INT_MAX; + +struct LOC_CONTROL +{ + LPWSTR wzControl; + int nX; + int nY; + int nWidth; + int nHeight; + LPWSTR wzText; +}; + +const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX; + +struct WIX_LOCALIZATION +{ + DWORD dwLangId; + + DWORD cLocStrings; + LOC_STRING* rgLocStrings; + + DWORD cLocControls; + LOC_CONTROL* rgLocControls; +}; + +/******************************************************************** + LocProbeForFile - Searches for a localization file on disk. + +*******************************************************************/ +HRESULT DAPI LocProbeForFile( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath + ); + +/******************************************************************** + LocLoadFromFile - Loads a localization file + +*******************************************************************/ +HRESULT DAPI LocLoadFromFile( + __in_z LPCWSTR wzWxlFile, + __out WIX_LOCALIZATION** ppWixLoc + ); + +/******************************************************************** + LocLoadFromResource - loads a localization file from a module's data + resource. + + NOTE: The resource data must be UTF-8 encoded. +*******************************************************************/ +HRESULT DAPI LocLoadFromResource( + __in HMODULE hModule, + __in_z LPCSTR szResource, + __out WIX_LOCALIZATION** ppWixLoc + ); + +/******************************************************************** + LocFree - free memory allocated when loading a localization file + +*******************************************************************/ +void DAPI LocFree( + __in_opt WIX_LOCALIZATION* pWixLoc + ); + +/******************************************************************** + LocLocalizeString - replace any #(loc.id) in a string with the + correct sub string +*******************************************************************/ +HRESULT DAPI LocLocalizeString( + __in const WIX_LOCALIZATION* pWixLoc, + __inout LPWSTR* psczInput + ); + +/******************************************************************** + LocGetControl - returns a control's localization information +*******************************************************************/ +HRESULT DAPI LocGetControl( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_CONTROL** ppLocControl + ); + +/******************************************************************** +LocGetString - returns a string's localization information +*******************************************************************/ +extern "C" HRESULT DAPI LocGetString( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_STRING** ppLocString + ); + +/******************************************************************** +LocAddString - adds a localization string +*******************************************************************/ +extern "C" HRESULT DAPI LocAddString( + __in WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzLocString, + __in BOOL bOverridable + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/logutil.h b/src/dutil/inc/logutil.h new file mode 100644 index 00000000..ee0cd065 --- /dev/null +++ b/src/dutil/inc/logutil.h @@ -0,0 +1,190 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define LogExitOnFailure(x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } + +#define LogExitOnRootFailure(x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, f, __VA_ARGS__); goto LExit; } + +typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)( + __in_z LPCSTR szString, + __in_opt LPVOID pvContext + ); + +// enums + +// structs + +// functions +BOOL DAPI IsLogInitialized(); + +BOOL DAPI IsLogOpen(); + +void DAPI LogInitialize( + __in HMODULE hModule + ); + +HRESULT DAPI LogOpen( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzLog, + __in_z_opt LPCWSTR wzPostfix, + __in_z_opt LPCWSTR wzExt, + __in BOOL fAppend, + __in BOOL fHeader, + __out_z_opt LPWSTR* psczLogPath + ); + +void DAPI LogDisable(); + +void DAPI LogRedirect( + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, + __in_opt LPVOID pvContext + ); + +HRESULT DAPI LogRename( + __in_z LPCWSTR wzNewPath + ); + +void DAPI LogClose( + __in BOOL fFooter + ); + +void DAPI LogUninitialize( + __in BOOL fFooter + ); + +BOOL DAPI LogIsOpen(); + +HRESULT DAPI LogSetSpecialParams( + __in_z_opt LPCWSTR wzSpecialBeginLine, + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, + __in_z_opt LPCWSTR wzSpecialEndLine + ); + +REPORT_LEVEL DAPI LogSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fLogChange + ); + +REPORT_LEVEL DAPI LogGetLevel(); + +HRESULT DAPI LogGetPath( + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, + __in DWORD cchLogPath + ); + +HANDLE DAPI LogGetHandle(); + +HRESULT DAPIV LogString( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogStringArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPIV LogStringLine( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogStringLineArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPI LogIdModuleArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in va_list args + ); + +/* + * Wraps LogIdModuleArgs, so inline to save the function call + */ + +inline HRESULT LogId( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, dwLogId); + hr = LogIdModuleArgs(rl, dwLogId, NULL, args); + va_end(args); + + return hr; +} + + +/* + * Wraps LogIdModuleArgs, so inline to save the function call + */ + +inline HRESULT LogIdArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in va_list args + ) +{ + return LogIdModuleArgs(rl, dwLogId, NULL, args); +} + +HRESULT DAPIV LogErrorString( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogErrorStringArgs( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPI LogErrorIdModule( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzString1, + __in_z_opt LPCWSTR wzString2, + __in_z_opt LPCWSTR wzString3 + ); + +inline HRESULT LogErrorId( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_z_opt LPCWSTR wzString1 = NULL, + __in_z_opt LPCWSTR wzString2 = NULL, + __in_z_opt LPCWSTR wzString3 = NULL + ) +{ + return LogErrorIdModule(hrError, dwLogId, NULL, wzString1, wzString2, wzString3); +} + +HRESULT DAPI LogHeader(); + +HRESULT DAPI LogFooter(); + +HRESULT LogStringWorkRaw( + __in_z LPCSTR szLogData + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/memutil.h b/src/dutil/inc/memutil.h new file mode 100644 index 00000000..93e53228 --- /dev/null +++ b/src/dutil/inc/memutil.h @@ -0,0 +1,80 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseMem(p) if (p) { MemFree(p); } +#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; } + +HRESULT DAPI MemInitialize(); +void DAPI MemUninitialize(); + +LPVOID DAPI MemAlloc( + __in SIZE_T cbSize, + __in BOOL fZero + ); +LPVOID DAPI MemReAlloc( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero + ); +HRESULT DAPI MemReAllocSecure( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero, + __deref_out LPVOID* ppvNew + ); +HRESULT DAPI MemAllocArray( + __inout LPVOID* ppvArray, + __in SIZE_T cbArrayType, + __in DWORD dwItemCount + ); +HRESULT DAPI MemReAllocArray( + __inout LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwNewItemCount + ); +HRESULT DAPI MemEnsureArraySize( + __deref_out_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ); +HRESULT DAPI MemInsertIntoArray( + __deref_out_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __in DWORD dwInsertIndex, + __in DWORD cInsertItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ); +void DAPI MemRemoveFromArray( + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __in DWORD dwRemoveIndex, + __in DWORD cRemoveItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in BOOL fPreserveOrder + ); +void DAPI MemArraySwapItems( + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __in DWORD dwIndex1, + __in DWORD dwIndex2, + __in SIZE_T cbArrayType + ); + +HRESULT DAPI MemFree( + __in LPVOID pv + ); +SIZE_T DAPI MemSize( + __in LPCVOID pv + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/metautil.h b/src/dutil/inc/metautil.h new file mode 100644 index 00000000..31f9ff5c --- /dev/null +++ b/src/dutil/inc/metautil.h @@ -0,0 +1,52 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + +// prototypes +HRESULT DAPI MetaFindWebBase( + __in IMSAdminBaseW* piMetabase, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); +HRESULT DAPI MetaFindFreeWebBase( + __in IMSAdminBaseW* piMetabase, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); + +HRESULT DAPI MetaOpenKey( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __in DWORD dwAccess, + __in DWORD cRetries, + __out METADATA_HANDLE* pmh + ); +HRESULT DAPI MetaGetValue( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __inout METADATA_RECORD* pmr + ); +void DAPI MetaFreeValue( + __in METADATA_RECORD* pmr + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/monutil.h b/src/dutil/inc/monutil.h new file mode 100644 index 00000000..f644e205 --- /dev/null +++ b/src/dutil/inc/monutil.h @@ -0,0 +1,108 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseMon(mh) if (mh) { MonDestroy(mh); } +#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; } + +typedef void* MON_HANDLE; +typedef const void* C_MON_HANDLE; + +// Defined in regutil.h +enum REG_KEY_BITNESS; + +extern const int MON_HANDLE_BYTES; + +// Note: callbacks must be implemented in a thread-safe manner. They will be called asynchronously by a MonUtil-spawned thread. +// They must also be written to return as soon as possible - they are called from the waiter thread +typedef void (*PFN_MONGENERAL)( + __in HRESULT hr, + __in_opt LPVOID pvContext + ); +// This callback is not specific to any wait - it will notify client of any drive status changes, such as removable drive insertion / removal +typedef void (*PFN_MONDRIVESTATUS)( + __in WCHAR chDrive, + __in BOOL fArriving, + __in_opt LPVOID pvContext + ); +// 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, +// 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, +// so all consumers of MonUtil should be designed with this in mind. +typedef void (*PFN_MONDIRECTORY)( + __in HRESULT hr, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvDirectoryContext + ); +typedef void (*PFN_MONREGKEY)( + __in HRESULT hr, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvRegKeyContext + ); + +// Silence period allows you to avoid lots of notifications when a lot of writes are going on in a directory +// MonUtil will wait until the directory has been "silent" for at least dwSilencePeriodInMs milliseconds +// The drawback to setting this to a value higher than zero is that even single write notifications +// are delayed by this amount +HRESULT DAPI MonCreate( + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, + __in PFN_MONGENERAL vpfMonGeneral, + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, + __in_opt PFN_MONDIRECTORY vpfMonDirectory, + __in_opt PFN_MONREGKEY vpfMonRegKey, + __in_opt LPVOID pvContext + ); +// Don't add multiple identical waits! Not only is it wasteful and will cause multiple fires for the exact same change, it will also +// result in slightly odd behavior when you remove a duplicated wait (removing a wait may or may not remove multiple waits) +// 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. +// So instead, de-dupe your wait requests before sending them to MonUtil. +// Special notes for network waits: MonUtil can send false positive notifications (i.e. notifications when nothing had changed) if connection +// to the share is lost and reconnected, because MonUtil can't know for sure whether changes occurred while the connection was lost. +// Also, MonUtil will very every 20 minutes retry even successful network waits, because the underlying Win32 API cannot notify us if a remote server +// had its network cable unplugged or similar sudden failure. When we retry the successful network waits, we will also send a false positive notification, +// 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 +// 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. +HRESULT DAPI MonAddDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvDirectoryContext + ); +HRESULT DAPI MonAddRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvRegKeyContext + ); +HRESULT DAPI MonRemoveDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive + ); +HRESULT DAPI MonRemoveRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive + ); +void DAPI MonDestroy( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/osutil.h b/src/dutil/inc/osutil.h new file mode 100644 index 00000000..01f8d9cf --- /dev/null +++ b/src/dutil/inc/osutil.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum OS_VERSION +{ + OS_VERSION_UNKNOWN, + OS_VERSION_WINNT, + OS_VERSION_WIN2000, + OS_VERSION_WINXP, + OS_VERSION_WIN2003, + OS_VERSION_VISTA, + OS_VERSION_WIN2008, + OS_VERSION_WIN7, + OS_VERSION_WIN2008_R2, + OS_VERSION_FUTURE +} OS_VERSION; + +void DAPI OsGetVersion( + __out OS_VERSION* pVersion, + __out DWORD* pdwServicePack + ); +HRESULT DAPI OsCouldRunPrivileged( + __out BOOL* pfPrivileged + ); +HRESULT DAPI OsIsRunningPrivileged( + __out BOOL* pfPrivileged + ); +HRESULT DAPI OsIsUacEnabled( + __out BOOL* pfUacEnabled + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h new file mode 100644 index 00000000..76798172 --- /dev/null +++ b/src/dutil/inc/pathutil.h @@ -0,0 +1,232 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum PATH_EXPAND +{ + PATH_EXPAND_ENVIRONMENT = 0x0001, + PATH_EXPAND_FULLPATH = 0x0002, +} PATH_EXPAND; + + +/******************************************************************* + PathCommandLineAppend - appends a command line argument on to a + string such that ::CommandLineToArgv() will shred them correctly + (i.e. quote arguments with spaces in them). +********************************************************************/ +DAPI_(HRESULT) PathCommandLineAppend( + __deref_out_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ); + +/******************************************************************* + PathFile - returns a pointer to the file part of the path. +********************************************************************/ +DAPI_(LPWSTR) PathFile( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathExtension - returns a pointer to the extension part of the path + (including the dot). +********************************************************************/ +DAPI_(LPCWSTR) PathExtension( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathGetDirectory - extracts the directory from a path. +********************************************************************/ +DAPI_(HRESULT) PathGetDirectory( + __in_z LPCWSTR wzPath, + __out LPWSTR *psczDirectory + ); + +/******************************************************************* + PathExpand - gets the full path to a file resolving environment + variables along the way. +********************************************************************/ +DAPI_(HRESULT) PathExpand( + __out LPWSTR *psczFullPath, + __in_z LPCWSTR wzRelativePath, + __in DWORD dwResolveFlags + ); + +/******************************************************************* + PathPrefix - prefixes a full path with \\?\ or \\?\UNC as + appropriate. +********************************************************************/ +DAPI_(HRESULT) PathPrefix( + __inout LPWSTR *psczFullPath + ); + +/******************************************************************* + PathFixedBackslashTerminate - appends a \ if path does not have it + already, but fails if the buffer is + insufficient. +********************************************************************/ +DAPI_(HRESULT) PathFixedBackslashTerminate( + __inout_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD_PTR cchPath + ); + +/******************************************************************* + PathBackslashTerminate - appends a \ if path does not have it + already. +********************************************************************/ +DAPI_(HRESULT) PathBackslashTerminate( + __inout LPWSTR* psczPath + ); + +/******************************************************************* + PathForCurrentProcess - gets the full path to the currently executing + process or (optionally) a module inside the process. +********************************************************************/ +DAPI_(HRESULT) PathForCurrentProcess( + __inout LPWSTR *psczFullPath, + __in_opt HMODULE hModule + ); + +/******************************************************************* + PathRelativeToModule - gets the name of a file in the same + directory as the current process or (optionally) a module inside + the process +********************************************************************/ +DAPI_(HRESULT) PathRelativeToModule( + __inout LPWSTR *psczFullPath, + __in_opt LPCWSTR wzFileName, + __in_opt HMODULE hModule + ); + +/******************************************************************* + PathCreateTempFile + + Note: if wzDirectory is null, ::GetTempPath() will be used instead. + if wzFileNameTemplate is null, GetTempFileName() will be used instead. +*******************************************************************/ +DAPI_(HRESULT) PathCreateTempFile( + __in_opt LPCWSTR wzDirectory, + __in_opt __format_string LPCWSTR wzFileNameTemplate, + __in DWORD dwUniqueCount, + __in DWORD dwFileAttributes, + __out_opt LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ); + +/******************************************************************* + PathCreateTimeBasedTempFile - creates an empty temp file based on current + system time +********************************************************************/ +DAPI_(HRESULT) PathCreateTimeBasedTempFile( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzPrefix, + __in_z_opt LPCWSTR wzPostfix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ); + +/******************************************************************* + PathCreateTempDirectory + + Note: if wzDirectory is null, ::GetTempPath() will be used instead. +*******************************************************************/ +DAPI_(HRESULT) PathCreateTempDirectory( + __in_opt LPCWSTR wzDirectory, + __in __format_string LPCWSTR wzDirectoryNameTemplate, + __in DWORD dwUniqueCount, + __out LPWSTR* psczTempDirectory + ); + +/******************************************************************* + PathGetKnownFolder - returns the path to a well-known shell folder + +*******************************************************************/ +DAPI_(HRESULT) PathGetKnownFolder( + __in int csidl, + __out LPWSTR* psczKnownFolder + ); + +/******************************************************************* + PathIsAbsolute - returns true if the path is absolute; false + otherwise. +*******************************************************************/ +DAPI_(BOOL) PathIsAbsolute( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathConcat - like .NET's Path.Combine, lets you build up a path + one piece -- file or directory -- at a time. +*******************************************************************/ +DAPI_(HRESULT) PathConcat( + __in_opt LPCWSTR wzPath1, + __in_opt LPCWSTR wzPath2, + __deref_out_z LPWSTR* psczCombined + ); + +/******************************************************************* + PathEnsureQuoted - ensures that a path is quoted; optionally, + this function also terminates a directory with a backslash + if it is not already. +*******************************************************************/ +DAPI_(HRESULT) PathEnsureQuoted( + __inout LPWSTR* ppszPath, + __in BOOL fDirectory + ); + +/******************************************************************* + PathCompare - compares the fully expanded path of the two paths using + ::CompareStringW(). +*******************************************************************/ +DAPI_(HRESULT) PathCompare( + __in_z LPCWSTR wzPath1, + __in_z LPCWSTR wzPath2, + __out int* pnResult + ); + +/******************************************************************* + PathCompress - sets the compression state on an existing file or + directory. A no-op on file systems that don't + support compression. +*******************************************************************/ +DAPI_(HRESULT) PathCompress( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathGetHierarchyArray - allocates an array containing, + in order, every parent directory of the specified path, + ending with the actual input path + This function also works with registry subkeys +*******************************************************************/ +DAPI_(HRESULT) PathGetHierarchyArray( + __in_z LPCWSTR wzPath, + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczPathArray, + __inout LPUINT pcPathArray + ); + +/******************************************************************* + PathCanonicalizePath - wrapper around PathCanonicalizeW. +*******************************************************************/ +DAPI_(HRESULT) PathCanonicalizePath( + __in_z LPCWSTR wzPath, + __deref_out_z LPWSTR* psczCanonicalized + ); + +/******************************************************************* +PathDirectoryContainsPath - checks if wzPath is located inside + wzDirectory. +*******************************************************************/ +DAPI_(HRESULT) PathDirectoryContainsPath( + __in_z LPCWSTR wzDirectory, + __in_z LPCWSTR wzPath + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/perfutil.h b/src/dutil/inc/perfutil.h new file mode 100644 index 00000000..7557511d --- /dev/null +++ b/src/dutil/inc/perfutil.h @@ -0,0 +1,24 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + + +// functions +void DAPI PerfInitialize( + ); +void DAPI PerfClickTime( + __out_opt LARGE_INTEGER* pliElapsed + ); +double DAPI PerfConvertToSeconds( + __in const LARGE_INTEGER* pli + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/polcutil.h b/src/dutil/inc/polcutil.h new file mode 100644 index 00000000..13618043 --- /dev/null +++ b/src/dutil/inc/polcutil.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn"; + +/******************************************************************** +PolcReadNumber - reads a number from policy. + +NOTE: S_FALSE returned if policy not set. +NOTE: out is set to default on S_FALSE or any error. +********************************************************************/ +HRESULT DAPI PolcReadNumber( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in DWORD dwDefault, + __out DWORD* pdw + ); + +/******************************************************************** +PolcReadString - reads a string from policy. + +NOTE: S_FALSE returned if policy not set. +NOTE: out is set to default on S_FALSE or any error. +********************************************************************/ +HRESULT DAPI PolcReadString( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in_z_opt LPCWSTR wzDefault, + __deref_out_z LPWSTR* pscz + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/procutil.h b/src/dutil/inc/procutil.h new file mode 100644 index 00000000..00f3f358 --- /dev/null +++ b/src/dutil/inc/procutil.h @@ -0,0 +1,75 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs +typedef struct _PROC_FILESYSTEMREDIRECTION +{ + BOOL fDisabled; + LPVOID pvRevertState; +} PROC_FILESYSTEMREDIRECTION; + +HRESULT DAPI ProcElevated( + __in HANDLE hProcess, + __out BOOL* pfElevated + ); + +HRESULT DAPI ProcWow64( + __in HANDLE hProcess, + __out BOOL* pfWow64 + ); +HRESULT DAPI ProcDisableWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ); +HRESULT DAPI ProcRevertWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ); + +HRESULT DAPI ProcExec( + __in_z LPCWSTR wzExecutablePath, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out HANDLE *phProcess + ); +HRESULT DAPI ProcExecute( + __in_z LPWSTR wzCommand, + __out HANDLE *phProcess, + __out_opt HANDLE *phChildStdIn, + __out_opt HANDLE *phChildStdOutErr + ); +HRESULT DAPI ProcWaitForCompletion( + __in HANDLE hProcess, + __in DWORD dwTimeout, + __out DWORD *pReturnCode + ); +HRESULT DAPI ProcWaitForIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds, + __in DWORD dwMilliseconds + ); +HRESULT DAPI ProcCloseIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds + ); + +// following code in proc2utl.cpp due to dependency on PSAPI.DLL. +HRESULT DAPI ProcFindAllIdsFromExeName( + __in_z LPCWSTR wzExeName, + __out DWORD** ppdwProcessIds, + __out DWORD* pcProcessIds + ); + +// following code in proc3utl.cpp due to dependency on Wtsapi32.DLL. +HRESULT DAPI ProcExecuteAsInteractiveUser( + __in_z LPCWSTR wzExecutablePath, + __in_z LPCWSTR wzCommand, + __out HANDLE *phProcess + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/regutil.h b/src/dutil/inc/regutil.h new file mode 100644 index 00000000..897b9d03 --- /dev/null +++ b/src/dutil/inc/regutil.h @@ -0,0 +1,233 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; } + +typedef enum REG_KEY_BITNESS +{ + REG_KEY_DEFAULT = 0, + REG_KEY_32BIT = 1, + REG_KEY_64BIT = 2 +} REG_KEY_BITNESS; + +typedef LSTATUS (APIENTRY *PFN_REGCREATEKEYEXW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ); +typedef LSTATUS (APIENTRY *PFN_REGOPENKEYEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey + ); +typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)( + __in HKEY hKey, + __in DWORD dwIndex, + __out LPWSTR lpName, + __inout LPDWORD lpcName, + __reserved LPDWORD lpReserved, + __inout LPWSTR lpClass, + __inout_opt LPDWORD lpcClass, + __out_opt PFILETIME lpftLastWriteTime + ); +typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)( + __in HKEY hKey, + __in DWORD dwIndex, + __out LPWSTR lpValueName, + __inout LPDWORD lpcchValueName, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpType, + __out_opt LPBYTE lpData, + __out_opt LPDWORD lpcbData + ); +typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)( + __in HKEY hKey, + __out LPWSTR lpClass, + __inout_opt LPDWORD lpcClass, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpcSubKeys, + __out_opt LPDWORD lpcMaxSubKeyLen, + __out_opt LPDWORD lpcMaxClassLen, + __out_opt LPDWORD lpcValues, + __out_opt LPDWORD lpcMaxValueNameLen, + __out_opt LPDWORD lpcMaxValueLen, + __out_opt LPDWORD lpcbSecurityDescriptor, + __out_opt PFILETIME lpftLastWriteTime + ); +typedef LSTATUS (APIENTRY *PFN_REGQUERYVALUEEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpType, + __out_bcount_part_opt(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData, + __inout_opt LPDWORD lpcbData + ); +typedef LSTATUS (APIENTRY *PFN_REGSETVALUEEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName, + __reserved DWORD Reserved, + __in DWORD dwType, + __in_bcount_opt(cbData) CONST BYTE* lpData, + __in DWORD cbData + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName + ); + +HRESULT DAPI RegInitialize(); +void DAPI RegUninitialize(); + +void DAPI RegFunctionOverride( + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW + ); +HRESULT DAPI RegCreate( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ); +HRESULT DAPI RegCreateEx( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __in BOOL fVolatile, + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, + __out HKEY* phk, + __out_opt BOOL* pfCreated + ); +HRESULT DAPI RegOpen( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ); +HRESULT DAPI RegDelete( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fDeleteTree + ); +HRESULT DAPI RegKeyEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczKey + ); +HRESULT DAPI RegValueEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczName, + __out_opt DWORD *pdwType + ); +HRESULT DAPI RegGetType( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD *pdwType + ); +HRESULT DAPI RegReadBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T *pcbBuffer + ); +HRESULT DAPI RegReadString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_z LPWSTR* psczValue + ); +HRESULT DAPI RegReadStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_ecount_opt(pcStrings) LPWSTR** prgsczStrings, + __out DWORD *pcStrings + ); +HRESULT DAPI RegReadVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pdw64Version + ); +HRESULT DAPI RegReadNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD* pdwValue + ); +HRESULT DAPI RegReadQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pqwValue + ); +HRESULT DAPI RegWriteBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_bcount(cbBuffer) const BYTE *pbBuffer, + __in DWORD cbBuffer + ); +HRESULT DAPI RegWriteString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI RegWriteStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_ecount(cValues) LPWSTR *rgwzStrings, + __in DWORD cStrings + ); +HRESULT DAPI RegWriteStringFormatted( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in __format_string LPCWSTR szFormat, + ... + ); +HRESULT DAPI RegWriteNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD dwValue + ); +HRESULT DAPI RegWriteQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD64 qwValue + ); +HRESULT DAPI RegQueryKey( + __in HKEY hk, + __out_opt DWORD* pcSubKeys, + __out_opt DWORD* pcValues + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/resrutil.h b/src/dutil/inc/resrutil.h new file mode 100644 index 00000000..1f4d8e17 --- /dev/null +++ b/src/dutil/inc/resrutil.h @@ -0,0 +1,43 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI ResGetStringLangId( + __in_opt LPCWSTR wzPath, + __in UINT uID, + __out WORD *pwLangId + ); + +HRESULT DAPI ResReadString( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPWSTR* ppwzString + ); + +HRESULT DAPI ResReadStringAnsi( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPSTR* ppszString + ); + +HRESULT DAPI ResReadData( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szDataName, + __deref_out_bcount(*pcb) PVOID *ppv, + __out DWORD *pcb + ); + +HRESULT DAPI ResExportDataToFile( + __in_z LPCSTR szDataName, + __in_z LPCWSTR wzTargetFile, + __in DWORD dwCreationDisposition + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/reswutil.h b/src/dutil/inc/reswutil.h new file mode 100644 index 00000000..31435ae2 --- /dev/null +++ b/src/dutil/inc/reswutil.h @@ -0,0 +1,31 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI ResWriteString( + __in_z LPCWSTR wzResourceFile, + __in DWORD dwDataId, + __in_z LPCWSTR wzData, + __in WORD wLangId + ); + +HRESULT DAPI ResWriteData( + __in_z LPCWSTR wzResourceFile, + __in_z LPCSTR szDataName, + __in PVOID pData, + __in DWORD cbData + ); + +HRESULT DAPI ResImportDataFromFile( + __in_z LPCWSTR wzTargetFile, + __in_z LPCWSTR wzSourceFile, + __in_z LPCSTR szDataName + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/rexutil.h b/src/dutil/inc/rexutil.h new file mode 100644 index 00000000..77f5604a --- /dev/null +++ b/src/dutil/inc/rexutil.h @@ -0,0 +1,54 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// defines +#define FILETABLESIZE 40 + +// structs +struct MEM_FILE +{ + LPCBYTE vpStart; + UINT uiCurrent; + UINT uiLength; +}; + +typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE; + +typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); +typedef VOID (*REX_CALLBACK_WRITE)(UINT cb); + + +struct FAKE_FILE // used __in internal file table +{ + BOOL fUsed; + FAKE_FILE_TYPE fftType; + MEM_FILE mfFile; // State for memory file + HANDLE hFile; // Handle for disk file +}; + +// functions +HRESULT RexInitialize(); +void RexUninitialize(); + +HRESULT RexExtract( + __in_z LPCSTR szResource, + __in_z LPCWSTR wzExtractId, + __in_z LPCWSTR wzExtractDir, + __in_z LPCWSTR wzExtractName, + __in REX_CALLBACK_PROGRESS pfnProgress, + __in REX_CALLBACK_WRITE pfnWrite, + __in LPVOID pvContext + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/rmutil.h b/src/dutil/inc/rmutil.h new file mode 100644 index 00000000..ce7bf254 --- /dev/null +++ b/src/dutil/inc/rmutil.h @@ -0,0 +1,46 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _RMU_SESSION *PRMU_SESSION; + +HRESULT DAPI RmuJoinSession( + __out PRMU_SESSION *ppSession, + __in_z LPCWSTR wzSessionKey + ); + +HRESULT DAPI RmuAddFile( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzPath + ); + +HRESULT DAPI RmuAddProcessById( + __in PRMU_SESSION pSession, + __in DWORD dwProcessId + ); + +HRESULT DAPI RmuAddProcessesByName( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzProcessName + ); + +HRESULT DAPI RmuAddService( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzServiceName + ); + +HRESULT DAPI RmuRegisterResources( + __in PRMU_SESSION pSession + ); + +HRESULT DAPI RmuEndSession( + __in PRMU_SESSION pSession + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/rssutil.h b/src/dutil/inc/rssutil.h new file mode 100644 index 00000000..064ab147 --- /dev/null +++ b/src/dutil/inc/rssutil.h @@ -0,0 +1,89 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); } +#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; } + + +struct RSS_UNKNOWN_ATTRIBUTE +{ + LPWSTR wzNamespace; + LPWSTR wzAttribute; + LPWSTR wzValue; + + RSS_UNKNOWN_ATTRIBUTE* pNext; +}; + +struct RSS_UNKNOWN_ELEMENT +{ + LPWSTR wzNamespace; + LPWSTR wzElement; + LPWSTR wzValue; + + RSS_UNKNOWN_ATTRIBUTE* pAttributes; + RSS_UNKNOWN_ELEMENT* pNext; +}; + +struct RSS_ITEM +{ + LPWSTR wzTitle; + LPWSTR wzLink; + LPWSTR wzDescription; + + LPWSTR wzGuid; + FILETIME ftPublished; + + LPWSTR wzEnclosureUrl; + DWORD dwEnclosureSize; + LPWSTR wzEnclosureType; + + RSS_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct RSS_CHANNEL +{ + LPWSTR wzTitle; + LPWSTR wzLink; + LPWSTR wzDescription; + DWORD dwTimeToLive; + + RSS_UNKNOWN_ELEMENT* pUnknownElements; + + DWORD cItems; + RSS_ITEM rgItems[1]; +}; + +HRESULT DAPI RssInitialize( + ); + +void DAPI RssUninitialize( + ); + +HRESULT DAPI RssParseFromString( + __in_z LPCWSTR wzRssString, + __out RSS_CHANNEL **ppChannel + ); + +HRESULT DAPI RssParseFromFile( + __in_z LPCWSTR wzRssFile, + __out RSS_CHANNEL **ppChannel + ); + +// Adding this until we have the updated specstrings.h +#ifndef __in_xcount +#define __in_xcount(size) +#endif + +void DAPI RssFreeChannel( + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/sceutil.h b/src/dutil/inc/sceutil.h new file mode 100644 index 00000000..9d14eecf --- /dev/null +++ b/src/dutil/inc/sceutil.h @@ -0,0 +1,273 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef void* SCE_DATABASE_HANDLE; +typedef void* SCE_ROW_HANDLE; +typedef void* SCE_QUERY_HANDLE; +typedef void* SCE_QUERY_RESULTS_HANDLE; + +extern const int SCE_ROW_HANDLE_BYTES; +extern const int SCE_QUERY_HANDLE_BYTES; +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES; + +#define ReleaseSceRow(prrh) if (prrh) { SceFreeRow(prrh); } +#define ReleaseNullSceRow(prrh) if (prrh) { SceFreeRow(prrh); prrh = NULL; } +#define ReleaseSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); } +#define ReleaseNullSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); pqh = NULL; } +#define ReleaseSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); } +#define ReleaseNullSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); pqh = NULL; } + +struct SCE_COLUMN_SCHEMA +{ + LPCWSTR wzName; + DBTYPE dbtColumnType; + DWORD dwLength; + BOOL fPrimaryKey; // If this column is the primary key + BOOL fNullable; + BOOL fAutoIncrement; + BOOL fDescending; // If this column should be descending when used in an index (default is ascending) + + LPWSTR wzRelationName; + DWORD dwForeignKeyTable; + DWORD dwForeignKeyColumn; +}; + +struct SCE_INDEX_SCHEMA +{ + LPWSTR wzName; + + DWORD *rgColumns; + DWORD cColumns; +}; + +struct SCE_TABLE_SCHEMA +{ + LPCWSTR wzName; + DWORD cColumns; + SCE_COLUMN_SCHEMA *rgColumns; + + DWORD cIndexes; + SCE_INDEX_SCHEMA *rgIndexes; + + // Internal to SCEUtil - consumers shouldn't access or modify + // TODO: enforce / hide in a handle of some sort? + IRowset *pIRowset; + IRowsetChange *pIRowsetChange; +}; + +struct SCE_DATABASE_SCHEMA +{ + DWORD cTables; + SCE_TABLE_SCHEMA *rgTables; +}; + +struct SCE_DATABASE +{ + SCE_DATABASE_HANDLE sdbHandle; + SCE_DATABASE_SCHEMA *pdsSchema; +}; + +HRESULT DAPI SceCreateDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __deref_out SCE_DATABASE **ppDatabase + ); +HRESULT DAPI SceOpenDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __deref_out SCE_DATABASE **ppDatabase, + __in BOOL fReadOnly + ); +HRESULT DAPI SceEnsureDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __in SCE_DATABASE_SCHEMA *pdsSchema, + __deref_out SCE_DATABASE **ppDatabase + ); +HRESULT DAPI SceIsTableEmpty( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out BOOL *pfEmpty + ); +HRESULT DAPI SceGetFirstRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceGetNextRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceBeginTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceCommitTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceRollbackTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceDeleteRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI ScePrepareInsert( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceFinishUpdate( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ); +HRESULT DAPI SceSetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI SceSetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD dwValue + ); +HRESULT DAPI SceSetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD64 qwValue + ); +HRESULT DAPI SceSetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const BOOL fValue + ); +HRESULT DAPI SceSetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI SceSetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const SYSTEMTIME *pst + ); +HRESULT DAPI SceSetColumnNull( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex + ); +HRESULT DAPI SceGetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbBuffer, + __inout SIZE_T *pcbBuffer + ); +HRESULT DAPI SceGetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD *pdwValue + ); +HRESULT DAPI SceGetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD64 *pqwValue + ); +HRESULT DAPI SceGetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out BOOL *pfValue + ); +HRESULT DAPI SceGetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_z LPWSTR *psczValue + ); +HRESULT DAPI SceGetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out SYSTEMTIME *pst + ); +HRESULT DAPI SceBeginQuery( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __in DWORD dwIndex, + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle + ); +HRESULT DAPI SceSetQueryColumnBinary( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI SceSetQueryColumnDword( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD dwValue + ); +HRESULT DAPI SceSetQueryColumnQword( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD64 qwValue + ); +HRESULT DAPI SceSetQueryColumnBool( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const BOOL fValue + ); +HRESULT DAPI SceSetQueryColumnString( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_z_opt LPCWSTR wzString + ); +HRESULT DAPI SceSetQueryColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in const SYSTEMTIME *pst + ); +HRESULT DAPI SceSetQueryColumnEmpty( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle + ); +HRESULT DAPI SceRunQueryExact( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceRunQueryRange( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle + ); +HRESULT DAPI SceGetNextResultRow( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +void DAPI SceCloseTable( + __in SCE_TABLE_SCHEMA *pTable + ); +// Returns whether the data in the database changed. Ignores schema changes. +BOOL DAPI SceDatabaseChanged( + __in SCE_DATABASE *pDatabase + ); +// Resets the database changed flag +void DAPI SceResetDatabaseChanged( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceCloseDatabase( + __in SCE_DATABASE *pDatabase + ); +void DAPI SceFreeRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle + ); +void DAPI SceFreeQuery( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle + ); +void DAPI SceFreeQueryResults( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/sczutil.h b/src/dutil/inc/sczutil.h new file mode 100644 index 00000000..fcfbd13a --- /dev/null +++ b/src/dutil/inc/sczutil.h @@ -0,0 +1,30 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +class PSCZ +{ +public: + PSCZ() : m_scz(NULL) { } + + ~PSCZ() { ReleaseNullStr(m_scz); } + + operator LPWSTR() { return m_scz; } + + operator LPCWSTR() { return m_scz; } + + operator bool() { return NULL != m_scz; } + + LPWSTR* operator &() { return &m_scz; } + + bool operator !() { return !m_scz; } + + WCHAR operator *() { return *m_scz; } + + LPWSTR Detach() { LPWSTR scz = m_scz; m_scz = NULL; return scz; } + +private: + LPWSTR m_scz; +}; +#endif //__cplusplus diff --git a/src/dutil/inc/shelutil.h b/src/dutil/inc/shelutil.h new file mode 100644 index 00000000..21e82672 --- /dev/null +++ b/src/dutil/inc/shelutil.h @@ -0,0 +1,47 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifndef REFKNOWNFOLDERID +#define REFKNOWNFOLDERID REFGUID +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ); + +void DAPI ShelFunctionOverride( + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW + ); +HRESULT DAPI ShelExec( + __in_z LPCWSTR wzTargetPath, + __in_opt LPCWSTR wzParameters, + __in_opt LPCWSTR wzVerb, + __in_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd, + __in_opt HWND hwndParent, + __out_opt HANDLE* phProcess + ); +HRESULT DAPI ShelExecUnelevated( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd + ); +HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ); +HRESULT DAPI ShelGetKnownFolder( + __out_z LPWSTR* psczFolderPath, + __in REFKNOWNFOLDERID rfidFolder + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/sqlutil.h b/src/dutil/inc/sqlutil.h new file mode 100644 index 00000000..ddf09323 --- /dev/null +++ b/src/dutil/inc/sqlutil.h @@ -0,0 +1,136 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +// Adding this until the SQL annotations are published to specstrings.h +#ifndef __sql_command +#define __sql_command +#endif + +// structs +struct SQL_FILESPEC +{ + WCHAR wzName[MAX_PATH]; + WCHAR wzFilename[MAX_PATH]; + WCHAR wzSize[MAX_PATH]; + WCHAR wzMaxSize[MAX_PATH]; + WCHAR wzGrow[MAX_PATH]; +}; + + +// functions +HRESULT DAPI SqlConnectDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out IDBCreateSession** ppidbSession + ); +HRESULT DAPI SqlStartTransaction( + __in IDBCreateSession* pidbSession, + __out IDBCreateCommand** ppidbCommand, + __out ITransaction** ppit + ); +HRESULT DAPI SqlEndTransaction( + __in ITransaction* pit, + __in BOOL fCommit + ); +HRESULT DAPI SqlDatabaseExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDatabaseExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlDatabaseEnsureExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDatabaseEnsureExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlCreateDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionCreateDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlDropDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDropDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionExecuteQuery( + __in IDBCreateSession* pidbSession, + __in __sql_command LPCWSTR wzSql, + __out_opt IRowset** ppirs, + __out_opt DBROWCOUNT* pcRows, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlCommandExecuteQuery( + __in IDBCreateCommand* pidbCommand, + __in __sql_command LPCWSTR wzSql, + __out IRowset** ppirs, + __out DBROWCOUNT* pcRows + ); +HRESULT DAPI SqlGetErrorInfo( + __in IUnknown* pObjectWithError, + __in REFIID IID_InterfaceWithError, + __in DWORD dwLocaleId, + __out_opt BSTR* pbstrErrorSource, + __out_opt BSTR* pbstrErrorDescription + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/srputil.h b/src/dutil/inc/srputil.h new file mode 100644 index 00000000..95e96231 --- /dev/null +++ b/src/dutil/inc/srputil.h @@ -0,0 +1,45 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum SRP_ACTION +{ + SRP_ACTION_UNKNOWN, + SRP_ACTION_UNINSTALL, + SRP_ACTION_INSTALL, + SRP_ACTION_MODIFY, +} SRP_ACTION; + + +/******************************************************************** + SrpInitialize - initializes system restore point functionality. + +*******************************************************************/ +DAPI_(HRESULT) SrpInitialize( + __in BOOL fInitializeComSecurity + ); + +/******************************************************************** + SrpUninitialize - uninitializes system restore point functionality. + +*******************************************************************/ +DAPI_(void) SrpUninitialize(); + +/******************************************************************** + SrpCreateRestorePoint - creates a system restore point. + +*******************************************************************/ +DAPI_(HRESULT) SrpCreateRestorePoint( + __in_z LPCWSTR wzApplicationName, + __in SRP_ACTION action + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h new file mode 100644 index 00000000..1a2ed1d8 --- /dev/null +++ b/src/dutil/inc/strutil.h @@ -0,0 +1,311 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseStr(pwz) if (pwz) { StrFree(pwz); } +#define ReleaseNullStr(pwz) if (pwz) { StrFree(pwz); pwz = NULL; } +#define ReleaseBSTR(bstr) if (bstr) { ::SysFreeString(bstr); } +#define ReleaseNullBSTR(bstr) if (bstr) { ::SysFreeString(bstr); bstr = NULL; } +#define ReleaseStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); } } +#define ReleaseNullStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); c = 0; rg = NULL; } } +#define ReleaseNullStrSecure(pwz) if (pwz) { StrSecureZeroFreeString(pwz); pwz = NULL; } + +#define DeclareConstBSTR(bstr_const, wz) const WCHAR bstr_const[] = { 0x00, 0x00, sizeof(wz)-sizeof(WCHAR), 0x00, wz } +#define UseConstBSTR(bstr_const) const_cast(bstr_const + 4) + +HRESULT DAPI StrAlloc( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ); +HRESULT DAPI StrAllocSecure( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ); +HRESULT DAPI StrTrimCapacity( + __deref_out_z LPWSTR* ppwz + ); +HRESULT DAPI StrTrimWhitespace( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource + ); +HRESULT DAPI StrAnsiAlloc( + __deref_out_ecount_part(cch, 0) LPSTR* ppz, + __in DWORD_PTR cch + ); +HRESULT DAPI StrAnsiTrimCapacity( + __deref_out_z LPSTR* ppz + ); +HRESULT DAPI StrAnsiTrimWhitespace( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR szSource + ); +HRESULT DAPI StrAllocString( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT DAPI StrAllocStringSecure( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT DAPI StrAnsiAllocString( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource, + __in UINT uiCodepage + ); +HRESULT DAPI StrAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCSTR szSource, + __in DWORD_PTR cchSource, + __in UINT uiCodepage + ); +HRESULT DAPI StrAnsiAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCSTR szSource, + __in DWORD_PTR cchSource + ); +HRESULT DAPI StrAllocPrefix( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzPrefix, + __in DWORD_PTR cchPrefix + ); +HRESULT DAPI StrAllocConcat( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT DAPI StrAllocConcatSecure( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT DAPI StrAnsiAllocConcat( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR pzSource, + __in DWORD_PTR cchSource + ); +HRESULT __cdecl StrAllocFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocConcatFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAnsiAllocFormatted( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI StrAllocFormattedArgs( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +HRESULT DAPI StrAllocFormattedArgsSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +HRESULT DAPI StrAnsiAllocFormattedArgs( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + __in va_list args + ); +HRESULT DAPI StrAllocFromError( + __inout LPWSTR *ppwzMessage, + __in HRESULT hrError, + __in_opt HMODULE hModule, + ... + ); + +HRESULT DAPI StrMaxLength( + __in LPCVOID p, + __out DWORD_PTR* pcch + ); +HRESULT DAPI StrSize( + __in LPCVOID p, + __out DWORD_PTR* pcb + ); + +HRESULT DAPI StrFree( + __in LPVOID p + ); + + +HRESULT DAPI StrReplaceStringAll( + __inout LPWSTR* ppwzOriginal, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ); +HRESULT DAPI StrReplaceString( + __inout LPWSTR* ppwzOriginal, + __inout DWORD* pdwStartIndex, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ); + +HRESULT DAPI StrHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in DWORD_PTR cbSource, + __out_ecount(cchDest) LPWSTR wzDest, + __in DWORD_PTR cchDest + ); +HRESULT DAPI StrAllocHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in DWORD_PTR cbSource, + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest + ); +HRESULT DAPI StrHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(cbDest) BYTE* pbDest, + __in DWORD_PTR cbDest + ); +HRESULT DAPI StrAllocHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(*pcbDest) BYTE** ppbDest, + __out_opt DWORD* pcbDest + ); + +HRESULT DAPI StrAllocBase85Encode( + __in_bcount_opt(cbSource) const BYTE* pbSource, + __in DWORD_PTR cbSource, + __deref_out_z LPWSTR* pwzDest + ); +HRESULT DAPI StrAllocBase85Decode( + __in_z LPCWSTR wzSource, + __deref_out_bcount(*pcbDest) BYTE** hbDest, + __out DWORD_PTR* pcbDest + ); + +HRESULT DAPI MultiSzLen( + __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, + __out DWORD_PTR* pcch + ); +HRESULT DAPI MultiSzPrepend( + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt DWORD_PTR *pcchMultiSz, + __in __nullnullterminated LPCWSTR pwzInsert + ); +HRESULT DAPI MultiSzFindSubstring( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzSubstring, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out_z LPCWSTR* ppwzFoundIn + ); +HRESULT DAPI MultiSzFindString( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzString, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound + ); +HRESULT DAPI MultiSzRemoveString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex + ); +HRESULT DAPI MultiSzInsertString( + __deref_inout_z LPWSTR* ppwzMultiSz, + __inout_opt DWORD_PTR *pcchMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzInsert + ); +HRESULT DAPI MultiSzReplaceString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzString + ); + +LPCWSTR DAPI wcsistr( + __in_z LPCWSTR wzString, + __in_z LPCWSTR wzCharSet + ); + +HRESULT DAPI StrStringToInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out SHORT* psOut + ); +HRESULT DAPI StrStringToUInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out USHORT* pusOut + ); +HRESULT DAPI StrStringToInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out INT* piOut + ); +HRESULT DAPI StrStringToUInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out UINT* puiOut + ); +HRESULT DAPI StrStringToInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out LONGLONG* pllOut + ); +HRESULT DAPI StrStringToUInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out ULONGLONG* pullOut + ); +void DAPI StrStringToUpper( + __inout_z LPWSTR wzIn + ); +void DAPI StrStringToLower( + __inout_z LPWSTR wzIn + ); +HRESULT DAPI StrAllocStringToUpperInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in int cchSource + ); +HRESULT DAPI StrAllocStringToLowerInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in int cchSource + ); + +HRESULT DAPI StrArrayAllocString( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); + +HRESULT DAPI StrArrayFree( + __in_ecount(cStrArray) LPWSTR *rgsczStrArray, + __in UINT cStrArray + ); + +HRESULT DAPI StrSplitAllocArray( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzDelim + ); + +HRESULT DAPI StrSecureZeroString( + __in LPWSTR pwz + ); +HRESULT DAPI StrSecureZeroFreeString( + __in LPWSTR pwz + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/svcutil.h b/src/dutil/inc/svcutil.h new file mode 100644 index 00000000..80d6326c --- /dev/null +++ b/src/dutil/inc/svcutil.h @@ -0,0 +1,21 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; } + + +HRESULT DAPI SvcQueryConfig( + __in SC_HANDLE sch, + __out QUERY_SERVICE_CONFIGW** ppConfig + ); + + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h new file mode 100644 index 00000000..5b3d4667 --- /dev/null +++ b/src/dutil/inc/thmutil.h @@ -0,0 +1,711 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; } + +typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)( + __in_z LPCWSTR wzCondition, + __out BOOL* pf, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)( + __in_z LPCWSTR wzFormat, + __inout LPWSTR* psczOut, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)( + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)( + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)( + __in_z LPCWSTR wzVariable, + __inout LPWSTR* psczValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)( + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in_opt LPVOID pvContext + ); + +typedef enum THEME_ACTION_TYPE +{ + THEME_ACTION_TYPE_BROWSE_DIRECTORY, + THEME_ACTION_TYPE_CHANGE_PAGE, + THEME_ACTION_TYPE_CLOSE_WINDOW, +} THEME_ACTION_TYPE; + +typedef enum THEME_CONTROL_DATA +{ + THEME_CONTROL_DATA_HOVER = 1, +} THEME_CONTROL_DATA; + +typedef enum THEME_CONTROL_TYPE +{ + THEME_CONTROL_TYPE_UNKNOWN, + THEME_CONTROL_TYPE_BILLBOARD, + THEME_CONTROL_TYPE_BUTTON, + THEME_CONTROL_TYPE_CHECKBOX, + THEME_CONTROL_TYPE_COMBOBOX, + THEME_CONTROL_TYPE_COMMANDLINK, + THEME_CONTROL_TYPE_EDITBOX, + THEME_CONTROL_TYPE_HYPERLINK, + THEME_CONTROL_TYPE_HYPERTEXT, + THEME_CONTROL_TYPE_IMAGE, + THEME_CONTROL_TYPE_LABEL, + THEME_CONTROL_TYPE_PANEL, + THEME_CONTROL_TYPE_PROGRESSBAR, + THEME_CONTROL_TYPE_RADIOBUTTON, + THEME_CONTROL_TYPE_RICHEDIT, + THEME_CONTROL_TYPE_STATIC, + THEME_CONTROL_TYPE_LISTVIEW, + THEME_CONTROL_TYPE_TREEVIEW, + THEME_CONTROL_TYPE_TAB, +} THEME_CONTROL_TYPE; + +typedef enum THEME_SHOW_PAGE_REASON +{ + THEME_SHOW_PAGE_REASON_DEFAULT, + THEME_SHOW_PAGE_REASON_CANCEL, + THEME_SHOW_PAGE_REASON_REFRESH, +} THEME_SHOW_PAGE_REASON; + + +struct THEME_COLUMN +{ + LPWSTR pszName; + UINT uStringId; + int nBaseWidth; + int nWidth; + BOOL fExpands; +}; + + +struct THEME_TAB +{ + LPWSTR pszName; + UINT uStringId; +}; + +struct THEME_ACTION +{ + LPWSTR sczCondition; + THEME_ACTION_TYPE type; + union + { + struct + { + LPWSTR sczVariableName; + } BrowseDirectory; + struct + { + LPWSTR sczPageName; + BOOL fCancel; + } ChangePage; + }; +}; + +struct THEME_CONDITIONAL_TEXT +{ + LPWSTR sczCondition; + LPWSTR sczText; +}; + +// THEME_ASSIGN_CONTROL_ID - Used to apply a specific id to a named control (usually +// to set the WM_COMMAND). +struct THEME_ASSIGN_CONTROL_ID +{ + WORD wId; // id to apply to control + LPCWSTR wzName; // name of control to match +}; + +const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned. + +struct THEME_CONTROL +{ + THEME_CONTROL_TYPE type; + + WORD wId; + WORD wPageId; + + LPWSTR sczName; // optional name for control, used to apply control id and link the control to a variable. + LPWSTR sczText; + LPWSTR sczTooltip; + LPWSTR sczNote; // optional text for command link + int nX; + int nY; + int nHeight; + int nWidth; + int nSourceX; + int nSourceY; + UINT uStringId; + + LPWSTR sczEnableCondition; + LPWSTR sczVisibleCondition; + BOOL fDisableVariableFunctionality; + + HBITMAP hImage; + HICON hIcon; + + // Don't free these; it's just a handle to the central image lists stored in THEME. The handle is freed once, there. + HIMAGELIST rghImageList[4]; + + DWORD dwStyle; + DWORD dwExtendedStyle; + DWORD dwInternalStyle; + + DWORD dwFontId; + + // child controls + DWORD cControls; + THEME_CONTROL* rgControls; + + // Used by billboard controls + WORD wBillboardInterval; + BOOL fBillboardLoops; + + // Used by button and command link controls + THEME_ACTION* rgActions; + DWORD cActions; + THEME_ACTION* pDefaultAction; + + // Used by hyperlink and owner-drawn button controls + DWORD dwFontHoverId; + DWORD dwFontSelectedId; + + // Used by listview controls + THEME_COLUMN *ptcColumns; + DWORD cColumns; + + // Used by radio button controls + BOOL fLastRadioButton; + LPWSTR sczValue; + LPWSTR sczVariable; + + // Used by tab controls + THEME_TAB *pttTabs; + DWORD cTabs; + + // Used by controls that have text + DWORD cConditionalText; + THEME_CONDITIONAL_TEXT* rgConditionalText; + + // Used by command link controls + DWORD cConditionalNotes; + THEME_CONDITIONAL_TEXT* rgConditionalNotes; + + // state variables that should be ignored + HWND hWnd; + DWORD dwData; // type specific data +}; + + +struct THEME_IMAGELIST +{ + LPWSTR sczName; + + HIMAGELIST hImageList; +}; + +struct THEME_SAVEDVARIABLE +{ + LPWSTR wzName; + LPWSTR sczValue; +}; + +struct THEME_PAGE +{ + WORD wId; + LPWSTR sczName; + + DWORD cControlIndices; + + DWORD cSavedVariables; + THEME_SAVEDVARIABLE* rgSavedVariables; +}; + +struct THEME_FONT +{ + HFONT hFont; + COLORREF crForeground; + HBRUSH hForeground; + COLORREF crBackground; + HBRUSH hBackground; +}; + + +struct THEME +{ + WORD wId; + + BOOL fAutoResize; + + DWORD dwStyle; + DWORD dwFontId; + HANDLE hIcon; + LPWSTR sczCaption; + int nHeight; + int nMinimumHeight; + int nWidth; + int nMinimumWidth; + int nSourceX; + int nSourceY; + UINT uStringId; + + HBITMAP hImage; + + DWORD cFonts; + THEME_FONT* rgFonts; + + DWORD cPages; + THEME_PAGE* rgPages; + + DWORD cImageLists; + THEME_IMAGELIST* rgImageLists; + + DWORD cControls; + THEME_CONTROL* rgControls; + + // internal state variables -- do not use outside ThmUtil.cpp + HWND hwndParent; // parent for loaded controls + HWND hwndHover; // current hwnd hovered over + DWORD dwCurrentPageId; + HWND hwndTooltip; + + // callback functions + PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition; + PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString; + PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable; + PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable; + PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable; + PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable; + + LPVOID pvVariableContext; +}; + + +/******************************************************************** + ThemeInitialize - initialized theme management. + +*******************************************************************/ +DAPI_(HRESULT) ThemeInitialize( + __in_opt HMODULE hModule + ); + +/******************************************************************** + ThemeUninitialize - uninitialize theme management. + +*******************************************************************/ +DAPI_(void) ThemeUninitialize(); + +/******************************************************************** + ThemeLoadFromFile - loads a theme from a loose file. + + *******************************************************************/ +DAPI_(HRESULT) ThemeLoadFromFile( + __in_z LPCWSTR wzThemeFile, + __out THEME** ppTheme + ); + +/******************************************************************** + ThemeLoadFromResource - loads a theme from a module's data resource. + + NOTE: The resource data must be UTF-8 encoded. +*******************************************************************/ +DAPI_(HRESULT) ThemeLoadFromResource( + __in_opt HMODULE hModule, + __in_z LPCSTR szResource, + __out THEME** ppTheme + ); + +/******************************************************************** + ThemeFree - frees any memory associated with a theme. + +*******************************************************************/ +DAPI_(void) ThemeFree( + __in THEME* pTheme + ); + +/******************************************************************** +ThemeRegisterVariableCallbacks - registers a context and callbacks + for working with variables. + +*******************************************************************/ +DAPI_(HRESULT) ThemeRegisterVariableCallbacks( + __in THEME* pTheme, + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, + __in_opt LPVOID pvContext + ); + +/******************************************************************** + ThemeLoadControls - creates the windows for all the theme controls. + +*******************************************************************/ +DAPI_(HRESULT) ThemeLoadControls( + __in THEME* pTheme, + __in HWND hwndParent, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ); + +/******************************************************************** + ThemeUnloadControls - resets all the theme control windows so the theme + controls can be reloaded. + +*******************************************************************/ +DAPI_(void) ThemeUnloadControls( + __in THEME* pTheme + ); + +/******************************************************************** + ThemeLocalize - Localizes all of the strings in the theme. + +*******************************************************************/ +DAPI_(HRESULT) ThemeLocalize( + __in THEME *pTheme, + __in const WIX_LOCALIZATION *pLocStringSet + ); + +DAPI_(HRESULT) ThemeLoadStrings( + __in THEME* pTheme, + __in HMODULE hResModule + ); + +/******************************************************************** + ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file. + + *******************************************************************/ +DAPI_(HRESULT) ThemeLoadRichEditFromFile( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCWSTR wzFileName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeLoadRichEditFromResource - Attach a richedit control to resource data. + + *******************************************************************/ +DAPI_(HRESULT) ThemeLoadRichEditFromResource( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeLoadRichEditFromResourceToHWnd - Attach a richedit control (by + HWND) to resource data. + + *******************************************************************/ +DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( + __in HWND hWnd, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeHandleKeyboardMessage - will translate the message using the active + accelerator table. + +*******************************************************************/ +DAPI_(BOOL) ThemeHandleKeyboardMessage( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in MSG* pMsg + ); + +/******************************************************************** + ThemeDefWindowProc - replacement for DefWindowProc() when using theme. + +*******************************************************************/ +LRESULT CALLBACK ThemeDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +/******************************************************************** + ThemeGetPageIds - gets the page ids for the theme via page names. + +*******************************************************************/ +DAPI_(void) ThemeGetPageIds( + __in const THEME* pTheme, + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, + __inout_ecount(cGetPages) DWORD* rgdwPageIds, + __in DWORD cGetPages + ); + +/******************************************************************** + ThemeGetPage - gets a theme page by id. + + *******************************************************************/ +DAPI_(THEME_PAGE*) ThemeGetPage( + __in const THEME* pTheme, + __in DWORD dwPage + ); + +/******************************************************************** + ThemeShowPage - shows or hides all of the controls in the page at one time. + + *******************************************************************/ +DAPI_(HRESULT) ThemeShowPage( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow + ); + +/******************************************************************** +ThemeShowPageEx - shows or hides all of the controls in the page at one time. + When using variables, TSPR_CANCEL reverts any changes made. + TSPR_REFRESH forces reevaluation of conditions. + It is expected that the current page is hidden before + showing a new page. + +*******************************************************************/ +DAPI_(HRESULT) ThemeShowPageEx( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow, + __in THEME_SHOW_PAGE_REASON reason + ); + + +/******************************************************************** +ThemeShowChild - shows a control's specified child control, hiding the rest. + +*******************************************************************/ +DAPI_(void) ThemeShowChild( + __in THEME* pTheme, + __in THEME_CONTROL* pParentControl, + __in DWORD dwIndex + ); + +/******************************************************************** + ThemeControlExists - check if a control with the specified id exists. + + *******************************************************************/ +DAPI_(BOOL) ThemeControlExists( + __in const THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeControlEnable - enables/disables a control. + + *******************************************************************/ +DAPI_(void) ThemeControlEnable( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fEnable + ); + +/******************************************************************** + ThemeControlEnabled - returns whether a control is enabled/disabled. + + *******************************************************************/ +DAPI_(BOOL) ThemeControlEnabled( + __in THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeControlElevates - sets/removes the shield icon on a control. + + *******************************************************************/ +DAPI_(void) ThemeControlElevates( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fElevates + ); + +/******************************************************************** + ThemeShowControl - shows/hides a control. + + *******************************************************************/ +DAPI_(void) ThemeShowControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ); + +/******************************************************************** +ThemeShowControlEx - shows/hides a control with support for +conditional text and notes. + +*******************************************************************/ +DAPI_(void) ThemeShowControlEx( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ); + +/******************************************************************** + ThemeControlVisible - returns whether a control is visible. + + *******************************************************************/ +DAPI_(BOOL) ThemeControlVisible( + __in THEME* pTheme, + __in DWORD dwControl + ); + +DAPI_(BOOL) ThemePostControlMessage( + __in THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +DAPI_(LRESULT) ThemeSendControlMessage( + __in const THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +/******************************************************************** + ThemeDrawBackground - draws the theme background. + +*******************************************************************/ +DAPI_(HRESULT) ThemeDrawBackground( + __in THEME* pTheme, + __in PAINTSTRUCT* pps + ); + +/******************************************************************** + ThemeDrawControl - draw an owner drawn control. + +*******************************************************************/ +DAPI_(HRESULT) ThemeDrawControl( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis + ); + +/******************************************************************** + ThemeHoverControl - mark a control as hover. + +*******************************************************************/ +DAPI_(BOOL) ThemeHoverControl( + __in THEME* pTheme, + __in HWND hwndParent, + __in HWND hwndControl + ); + +/******************************************************************** + ThemeIsControlChecked - gets whether a control is checked. Only + really useful for checkbox controls. + +*******************************************************************/ +DAPI_(BOOL) ThemeIsControlChecked( + __in THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeSetControlColor - sets the color of text for a control. + +*******************************************************************/ +DAPI_(BOOL) ThemeSetControlColor( + __in THEME* pTheme, + __in HDC hdc, + __in HWND hWnd, + __out HBRUSH* phBackgroundBrush + ); + +/******************************************************************** + ThemeSetProgressControl - sets the current percentage complete in a + progress bar control. + +*******************************************************************/ +DAPI_(HRESULT) ThemeSetProgressControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwProgressPercentage + ); + +/******************************************************************** + ThemeSetProgressControlColor - sets the current color of a + progress bar control. + +*******************************************************************/ +DAPI_(HRESULT) ThemeSetProgressControlColor( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwColorIndex + ); + +/******************************************************************** + ThemeSetTextControl - sets the text of a control. + +*******************************************************************/ +DAPI_(HRESULT) ThemeSetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __in_z_opt LPCWSTR wzText + ); + +/******************************************************************** +ThemeSetTextControl - sets the text of a control and optionally + invalidates the control. + +*******************************************************************/ +DAPI_(HRESULT) ThemeSetTextControlEx( + __in const THEME* pTheme, + __in DWORD dwControl, + __in BOOL fUpdate, + __in_z_opt LPCWSTR wzText + ); + +/******************************************************************** + ThemeGetTextControl - gets the text of a control. + +*******************************************************************/ +DAPI_(HRESULT) ThemeGetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __out_z LPWSTR* psczText + ); + +/******************************************************************** + ThemeUpdateCaption - updates the caption in the theme. + +*******************************************************************/ +DAPI_(HRESULT) ThemeUpdateCaption( + __in THEME* pTheme, + __in_z LPCWSTR wzCaption + ); + +/******************************************************************** + ThemeSetFocus - set the focus to the control supplied or the next + enabled control if it is disabled. + +*******************************************************************/ +DAPI_(void) ThemeSetFocus( + __in THEME* pTheme, + __in DWORD dwControl + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/timeutil.h b/src/dutil/inc/timeutil.h new file mode 100644 index 00000000..3655c00a --- /dev/null +++ b/src/dutil/inc/timeutil.h @@ -0,0 +1,38 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI TimeFromString( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ); +HRESULT DAPI TimeFromString3339( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ); +HRESULT DAPI TimeCurrentTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ); +HRESULT DAPI TimeCurrentDateTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ); +HRESULT DAPI TimeSystemDateTime( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in BOOL fGMT + ); +HRESULT DAPI TimeSystemToDateTimeString( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in LCID locale + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/uncutil.h b/src/dutil/inc/uncutil.h new file mode 100644 index 00000000..6516a801 --- /dev/null +++ b/src/dutil/inc/uncutil.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************* + UncConvertFromMountedDrive - Converts the string in-place from a + mounted drive path to a UNC path +*******************************************************************/ +DAPI_(HRESULT) UncConvertFromMountedDrive( + __inout LPWSTR *psczUNCPath, + __in LPCWSTR sczMountedDrivePath + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/uriutil.h b/src/dutil/inc/uriutil.h new file mode 100644 index 00000000..52e78308 --- /dev/null +++ b/src/dutil/inc/uriutil.h @@ -0,0 +1,100 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include "wininet.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum URI_PROTOCOL +{ + URI_PROTOCOL_UNKNOWN, + URI_PROTOCOL_FILE, + URI_PROTOCOL_FTP, + URI_PROTOCOL_HTTP, + URI_PROTOCOL_HTTPS, + URI_PROTOCOL_LOCAL, + URI_PROTOCOL_UNC +} URI_PROTOCOL; + +typedef struct _URI_INFO +{ + INTERNET_SCHEME scheme; + LPWSTR sczHostName; + INTERNET_PORT port; + LPWSTR sczUser; + LPWSTR sczPassword; + LPWSTR sczPath; + LPWSTR sczQueryString; +} URI_INFO; + + +HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ); + +HRESULT DAPI UriCrack( + __in_z LPCWSTR wzUri, + __out_opt INTERNET_SCHEME* pScheme, + __deref_opt_out_z LPWSTR* psczHostName, + __out_opt INTERNET_PORT* pPort, + __deref_opt_out_z LPWSTR* psczUser, + __deref_opt_out_z LPWSTR* psczPassword, + __deref_opt_out_z LPWSTR* psczPath, + __deref_opt_out_z LPWSTR* psczQueryString + ); + +HRESULT DAPI UriCrackEx( + __in_z LPCWSTR wzUri, + __in URI_INFO* pUriInfo + ); + +void DAPI UriInfoUninitialize( + __in URI_INFO* pUriInfo + ); + +HRESULT DAPI UriCreate( + __inout_z LPWSTR* psczUri, + __in INTERNET_SCHEME scheme, + __in_z_opt LPWSTR wzHostName, + __in INTERNET_PORT port, + __in_z_opt LPWSTR wzUser, + __in_z_opt LPWSTR wzPassword, + __in_z_opt LPWSTR wzPath, + __in_z_opt LPWSTR wzQueryString + ); + +HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ); + +HRESULT DAPI UriFile( + __deref_out_z LPWSTR* psczFile, + __in_z LPCWSTR wzUri + ); + +HRESULT DAPI UriProtocol( + __in_z LPCWSTR wzUri, + __out URI_PROTOCOL* pProtocol + ); + +HRESULT DAPI UriRoot( + __in_z LPCWSTR wzUri, + __out LPWSTR* ppwzRoot, + __out_opt URI_PROTOCOL* pProtocol + ); + +HRESULT DAPI UriResolve( + __in_z LPCWSTR wzUri, + __in_opt LPCWSTR wzBaseUri, + __out LPWSTR* ppwzResolvedUri, + __out_opt const URI_PROTOCOL* pResolvedProtocol + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dutil/inc/userutil.h b/src/dutil/inc/userutil.h new file mode 100644 index 00000000..2c86d229 --- /dev/null +++ b/src/dutil/inc/userutil.h @@ -0,0 +1,32 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI UserBuildDomainUserName( + __out_ecount_z(cchDest) LPWSTR wzDest, + __in int cchDest, + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain + ); + +HRESULT DAPI UserCheckIsMember( + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain, + __in_z LPCWSTR pwzGroupName, + __in_z LPCWSTR pwzGroupDomain, + __out LPBOOL lpfMember + ); + +HRESULT DAPI UserCreateADsPath( + __in_z LPCWSTR wzObjectDomain, + __in_z LPCWSTR wzObjectName, + __out BSTR *pbstrAdsPath + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/varutil.h b/src/dutil/inc/varutil.h new file mode 100644 index 00000000..86d0aca0 --- /dev/null +++ b/src/dutil/inc/varutil.h @@ -0,0 +1,126 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +#define ReleaseVariables(vh) if (vh) { VarDestroy(vh, NULL); } +#define ReleaseVariableValue(v) if (v) { VarFreeValue(v); } +#define ReleaseNullVariables(vh) if (vh) { VarDestroy(vh, NULL); vh = NULL; } +#define ReleaseNullVariableValue(v) if (v) { VarFreeValue(v); v = NULL; } + +typedef void* VARIABLE_ENUM_HANDLE; +typedef void* VARIABLES_HANDLE; +typedef const void* C_VARIABLES_HANDLE; + +extern const int VARIABLE_ENUM_HANDLE_BYTES; +extern const int VARIABLES_HANDLE_BYTES; + +typedef void(*PFN_FREEVARIABLECONTEXT)( + __in LPVOID pvContext + ); + +typedef enum VARIABLE_VALUE_TYPE +{ + VARIABLE_VALUE_TYPE_NONE, + VARIABLE_VALUE_TYPE_NUMERIC, + VARIABLE_VALUE_TYPE_STRING, + VARIABLE_VALUE_TYPE_VERSION, +} VARIABLE_VALUE_TYPE; + +typedef struct _VARIABLE_VALUE +{ + VARIABLE_VALUE_TYPE type; + union + { + LONGLONG llValue; + DWORD64 qwValue; + LPWSTR sczValue; + }; + BOOL fHidden; + LPVOID pvContext; +} VARIABLE_VALUE; + +HRESULT DAPI VarCreate( + __out_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE* ppVariables + ); +void DAPI VarDestroy( + __in_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE pVariables, + __in_opt PFN_FREEVARIABLECONTEXT vpfFreeVariableContext + ); +void DAPI VarFreeValue( + __in VARIABLE_VALUE* pValue + ); +HRESULT DAPI VarEscapeString( + __in_z LPCWSTR wzIn, + __out_z LPWSTR* psczOut + ); +HRESULT DAPI VarFormatString( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut + ); +HRESULT DAPI VarGetFormatted( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI VarGetNumeric( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ); +HRESULT DAPI VarGetString( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI VarGetVersion( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64* pqwValue + ); +HRESULT DAPI VarGetValue( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out VARIABLE_VALUE** ppValue + ); +HRESULT DAPI VarSetNumeric( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue + ); +HRESULT DAPI VarSetString( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI VarSetVersion( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64 qwValue + ); +HRESULT DAPI VarSetValue( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in VARIABLE_VALUE* pValue + ); +HRESULT DAPI VarStartEnum( + __in VARIABLES_HANDLE pVariables, + __out_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE* ppEnum, + __out VARIABLE_VALUE** ppValue + ); +HRESULT DAPI VarNextVariable( + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum, + __out VARIABLE_VALUE** ppValue + ); +void DAPI VarFinishEnum( + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/dutil/inc/wiutil.h b/src/dutil/inc/wiutil.h new file mode 100644 index 00000000..4264b815 --- /dev/null +++ b/src/dutil/inc/wiutil.h @@ -0,0 +1,373 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// constants + +#define IDNOACTION 0 +#define WIU_MB_OKIGNORECANCELRETRY 0xE + +#define MAX_DARWIN_KEY 73 +#define MAX_DARWIN_COLUMN 255 + +#define WIU_LOG_DEFAULT INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | \ + INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_RESOLVESOURCE | \ + INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | \ + INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROPERTYDUMP + +#define ReleaseMsi(h) if (h) { ::MsiCloseHandle(h); } +#define ReleaseNullMsi(h) if (h) { ::MsiCloseHandle(h); h = NULL; } + + +typedef enum WIU_RESTART +{ + WIU_RESTART_NONE, + WIU_RESTART_REQUIRED, + WIU_RESTART_INITIATED, +} WIU_RESTART; + +typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE +{ + WIU_MSI_EXECUTE_MESSAGE_NONE, + WIU_MSI_EXECUTE_MESSAGE_PROGRESS, + WIU_MSI_EXECUTE_MESSAGE_ERROR, + WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE, + WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE, +} WIU_MSI_EXECUTE_MESSAGE_TYPE; + + +// structures + +typedef struct _WIU_MSI_EXECUTE_MESSAGE +{ + WIU_MSI_EXECUTE_MESSAGE_TYPE type; + DWORD dwAllowedResults; + + DWORD cData; + LPCWSTR* rgwzData; + + INT nResultRecommendation; // recommended return result for this message based on analysis of real world installs. + + union + { + struct + { + DWORD dwPercentage; + } progress; + struct + { + DWORD dwErrorCode; + LPCWSTR wzMessage; + } error; + struct + { + INSTALLMESSAGE mt; + LPCWSTR wzMessage; + } msiMessage; + struct + { + DWORD cFiles; + LPCWSTR* rgwzFiles; + } msiFilesInUse; + }; +} WIU_MSI_EXECUTE_MESSAGE; + +typedef struct _WIU_MSI_PROGRESS +{ + DWORD dwTotal; + DWORD dwCompleted; + DWORD dwStep; + BOOL fMoveForward; + BOOL fEnableActionData; + BOOL fScriptInProgress; +} WIU_MSI_PROGRESS; + + +typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ); + +typedef struct _WIU_MSI_EXECUTE_CONTEXT +{ + BOOL fRollback; + PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; + LPVOID pvContext; + WIU_MSI_PROGRESS rgMsiProgress[64]; + DWORD dwCurrentProgressIndex; + + INSTALLUILEVEL previousInstallUILevel; + HWND hwndPreviousParentWindow; + INSTALLUI_HANDLERW pfnPreviousExternalUI; + INSTALLUI_HANDLER_RECORD pfnPreviousExternalUIRecord; + + BOOL fSetPreviousExternalUIRecord; + BOOL fSetPreviousExternalUI; +} WIU_MSI_EXECUTE_CONTEXT; + + +// typedefs +typedef UINT (WINAPI *PFN_MSIENABLELOGW)( + __in DWORD dwLogMode, + __in_z LPCWSTR szLogFile, + __in DWORD dwLogAttributes + ); +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout LPDWORD pcchValue + ); +typedef INSTALLSTATE (WINAPI *PFN_MSIGETCOMPONENTPATHW)( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOEXW)( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); +typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)( + __in LPCWSTR szProduct, + __in LPCWSTR szFeature + ); +typedef UINT (WINAPI *PFN_MSIGETPATCHINFOEXW)( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out_opt LPWSTR wzValue, + __inout DWORD* pcchValue + ); +typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in DWORD cPatchInfo, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo + ); +typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)( + __in_z LPCWSTR wzProductPackagePath, + __in DWORD cPatchInfo, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo + ); +typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)( + __in LPCWSTR szPackagePath, + __in_opt LPCWSTR szCommandLine + ); +typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)( + __in LPCWSTR szProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_opt LPCWSTR szCommandLine + ); +typedef UINT (WINAPI *PFN_MSIREMOVEPATCHESW)( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in INSTALLTYPE eUninstallType, + __in_z_opt LPCWSTR szPropertyList + ); +typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)( + __in INSTALLUILEVEL dwUILevel, + __inout_opt HWND *phWnd + ); +typedef UINT (WINAPI *PFN_MSISETEXTERNALUIRECORD)( + __in_opt INSTALLUI_HANDLER_RECORD puiHandler, + __in DWORD dwMessageFilter, + __in_opt LPVOID pvContext, + __out_opt PINSTALLUI_HANDLER_RECORD ppuiPrevHandler + ); +typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)( + __in_opt INSTALLUI_HANDLERW puiHandler, + __in DWORD dwMessageFilter, + __in_opt LPVOID pvContext + ); +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf + ); +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSEXW)( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ); + +typedef UINT (WINAPI *PFN_MSIENUMRELATEDPRODUCTSW)( + __in LPCWSTR lpUpgradeCode, + __reserved DWORD dwReserved, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf + ); +typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)( + __in LPCWSTR szProductCodeOrPatchCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwOptions, + __in LPCWSTR szSource, + __in_opt DWORD dwIndex + ); + + +HRESULT DAPI WiuInitialize( + ); +void DAPI WiuUninitialize( + ); +void DAPI WiuFunctionOverride( + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW + ); +HRESULT DAPI WiuGetComponentPath( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI WiuLocateComponent( + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI WiuQueryFeatureState( + __in_z LPCWSTR wzProduct, + __in_z LPCWSTR wzFeature, + __out INSTALLSTATE* pInstallState + ); +HRESULT DAPI WiuGetProductInfo( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetProductInfoEx( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetProductProperty( + __in MSIHANDLE hProduct, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetPatchInfoEx( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuDeterminePatchSequence( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ); +HRESULT DAPI WiuDetermineApplicablePatches( + __in_z LPCWSTR wzProductPackagePath, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ); +HRESULT DAPI WiuEnumProducts( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ); +HRESULT DAPI WiuEnumProductsEx( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ); +HRESULT DAPI WiuEnumRelatedProducts( + __in_z LPCWSTR wzUpgradeCode, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ); +HRESULT DAPI WiuEnumRelatedProductCodes( + __in_z LPCWSTR wzUpgradeCode, + __deref_out_ecount_opt(pcRelatedProducts) LPWSTR** prgsczProductCodes, + __out DWORD* pcRelatedProducts, + __in BOOL fReturnHighestVersionOnly + ); +HRESULT DAPI WiuEnableLog( + __in DWORD dwLogMode, + __in_z LPCWSTR wzLogFile, + __in DWORD dwLogAttributes + ); +HRESULT DAPI WiuInitializeExternalUI( + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in INSTALLUILEVEL internalUILevel, + __in HWND hwndParent, + __in LPVOID pvContext, + __in BOOL fRollback, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +void DAPI WiuUninitializeExternalUI( + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +HRESULT DAPI WiuConfigureProductEx( + __in_z LPCWSTR wzProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuInstallProduct( + __in_z LPCWSTR wzPackagPath, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuRemovePatches( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzPropertyList, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuSourceListAddSourceEx( + __in_z LPCWSTR wzProductCodeOrPatchCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwCode, + __in_z LPCWSTR wzSource, + __in_opt DWORD dwIndex + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/wuautil.h b/src/dutil/inc/wuautil.h new file mode 100644 index 00000000..b239c4e6 --- /dev/null +++ b/src/dutil/inc/wuautil.h @@ -0,0 +1,19 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT DAPI WuaPauseAutomaticUpdates(); + +HRESULT DAPI WuaResumeAutomaticUpdates(); + +HRESULT DAPI WuaRestartRequired( + __out BOOL* pfRestartRequired + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/dutil/inc/xmlutil.h b/src/dutil/inc/xmlutil.h new file mode 100644 index 00000000..3dc119bd --- /dev/null +++ b/src/dutil/inc/xmlutil.h @@ -0,0 +1,167 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; + +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}}; + +typedef enum XML_LOAD_ATTRIBUTE +{ + XML_LOAD_PRESERVE_WHITESPACE = 1, +} XML_LOAD_ATTRIBUTE; + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI XmlInitialize(); +void DAPI XmlUninitialize(); + +HRESULT DAPI XmlCreateElement( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzElementName, + __out IXMLDOMElement **ppixnElement + ); +HRESULT DAPI XmlCreateDocument( + __in_opt LPCWSTR pwzElementName, + __out IXMLDOMDocument** ppixdDocument, + __out_opt IXMLDOMElement** ppixeRootElement = NULL + ); +HRESULT DAPI XmlLoadDocument( + __in_z LPCWSTR wzDocument, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentEx( + __in_z LPCWSTR wzDocument, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromFile( + __in_z LPCWSTR wzPath, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromBuffer( + __in_bcount(cbSource) const BYTE* pbSource, + __in DWORD cbSource, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromFileEx( + __in_z LPCWSTR wzPath, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlSelectSingleNode( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNode **ppixnChild + ); +HRESULT DAPI XmlSetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in_z LPCWSTR pwzAttributeValue + ); +HRESULT DAPI XmlCreateTextNode( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzText, + __out IXMLDOMText **ppixnTextNode + ); +HRESULT DAPI XmlGetText( + __in IXMLDOMNode* pixnNode, + __deref_out_z BSTR* pbstrText + ); +HRESULT DAPI XmlGetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __deref_out_z BSTR* pbstrAttributeValue + ); +HRESULT DAPI XmlGetAttributeEx( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __deref_out_z LPWSTR* psczAttributeValue + ); +HRESULT DAPI XmlGetYesNoAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __out BOOL* pfYes + ); +HRESULT DAPI XmlGetAttributeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD* pdwValue + ); +HRESULT DAPI XmlGetAttributeNumberBase( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in int nBase, + __out DWORD* pdwValue + ); +HRESULT DAPI XmlGetAttributeLargeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD64* pdw64Value + ); +HRESULT DAPI XmlGetNamedItem( + __in IXMLDOMNamedNodeMap *pixnmAttributes, + __in_opt LPCWSTR wzName, + __out IXMLDOMNode **ppixnNamedItem + ); +HRESULT DAPI XmlSetText( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzText + ); +HRESULT DAPI XmlSetTextNumber( + __in IXMLDOMNode *pixnNode, + __in DWORD dwValue + ); +HRESULT DAPI XmlCreateChild( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR pwzElementType, + __out IXMLDOMNode** ppixnChild + ); +HRESULT DAPI XmlRemoveAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute + ); +HRESULT DAPI XmlSelectNodes( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNodeList **ppixnChild + ); +HRESULT DAPI XmlNextAttribute( + __in IXMLDOMNamedNodeMap* pixnnm, + __out IXMLDOMNode** pixnAttribute, + __deref_opt_out_z_opt BSTR* pbstrAttribute + ); +HRESULT DAPI XmlNextElement( + __in IXMLDOMNodeList* pixnl, + __out IXMLDOMNode** pixnElement, + __deref_opt_out_z_opt BSTR* pbstrElement + ); +HRESULT DAPI XmlRemoveChildren( + __in IXMLDOMNode* pixnSource, + __in_z LPCWSTR pwzXPath + ); +HRESULT DAPI XmlSaveDocument( + __in IXMLDOMDocument* pixdDocument, + __inout LPCWSTR wzPath + ); +HRESULT DAPI XmlSaveDocumentToBuffer( + __in IXMLDOMDocument* pixdDocument, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out DWORD* pcbDest + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inetutil.cpp b/src/dutil/inetutil.cpp new file mode 100644 index 00000000..69a0176a --- /dev/null +++ b/src/dutil/inetutil.cpp @@ -0,0 +1,137 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +/******************************************************************* + InternetGetSizeByHandle - returns size of file by url handle + +*******************************************************************/ +extern "C" HRESULT DAPI InternetGetSizeByHandle( + __in HINTERNET hiFile, + __out LONGLONG* pllSize + ) +{ + Assert(pllSize); + + HRESULT hr = S_OK; + DWORD dwSize; + DWORD cb; + + cb = sizeof(dwSize); + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast(&dwSize), &cb, NULL)) + { + ExitOnLastError(hr, "Failed to get size for internet file handle"); + } + + *pllSize = dwSize; +LExit: + return hr; +} + + +/******************************************************************* + InetGetCreateTimeByHandle - returns url creation time + +******************************************************************/ +extern "C" HRESULT DAPI InternetGetCreateTimeByHandle( + __in HINTERNET hiFile, + __out LPFILETIME pft + ) +{ + Assert(pft); + + HRESULT hr = S_OK; + SYSTEMTIME st = {0 }; + DWORD cb = sizeof(SYSTEMTIME); + + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast(&st), &cb, NULL)) + { + ExitWithLastError(hr, "failed to get create time for internet file handle"); + } + + if (!::SystemTimeToFileTime(&st, pft)) + { + ExitWithLastError(hr, "failed to convert system time to file time"); + } + +LExit: + return hr; +} + + +/******************************************************************* + InternetQueryInfoString - query info string + +*******************************************************************/ +extern "C" HRESULT DAPI InternetQueryInfoString( + __in HINTERNET hRequest, + __in DWORD dwInfo, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD_PTR cbValue = 0; + DWORD dwIndex = 0; + + // If nothing was provided start off with some arbitrary size. + if (!*psczValue) + { + hr = StrAlloc(psczValue, 64); + ExitOnFailure(hr, "Failed to allocate memory for value."); + } + + hr = StrSize(*psczValue, &cbValue); + ExitOnFailure(hr, "Failed to get size of value."); + + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), reinterpret_cast(&cbValue), &dwIndex)) + { + DWORD er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + cbValue += sizeof(WCHAR); // add one character for the null terminator. + + hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate value."); + + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), reinterpret_cast(&cbValue), &dwIndex)) + { + er = ::GetLastError(); + } + else + { + er = ERROR_SUCCESS; + } + } + + hr = HRESULT_FROM_WIN32(er); + ExitOnRootFailure(hr, "Failed to get query information."); + } + +LExit: + return hr; +} + + +/******************************************************************* + InternetQueryInfoNumber - query info number + +*******************************************************************/ +extern "C" HRESULT DAPI InternetQueryInfoNumber( + __in HINTERNET hRequest, + __in DWORD dwInfo, + __out LONG* plInfo + ) +{ + HRESULT hr = S_OK; + DWORD cbCode = sizeof(LONG); + DWORD dwIndex = 0; + + if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast(plInfo), &cbCode, &dwIndex)) + { + ExitWithLastError(hr, "Failed to get query information."); + } + +LExit: + return hr; +} diff --git a/src/dutil/iniutil.cpp b/src/dutil/iniutil.cpp new file mode 100644 index 00000000..c9ef6c3d --- /dev/null +++ b/src/dutil/iniutil.cpp @@ -0,0 +1,753 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const LPCWSTR wzSectionSeparator = L"\\"; + +struct INI_STRUCT +{ + LPWSTR sczPath; // the path to the INI file to be parsed + + LPWSTR sczOpenTagPrefix; // For regular ini, this would be '[' + LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']' + + LPWSTR sczValuePrefix; // for regular ini, this would be NULL + LPWSTR sczValueSeparator; // for regular ini, this would be '=' + + LPWSTR *rgsczValueSeparatorExceptions; + DWORD cValueSeparatorExceptions; + + LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';' + + INI_VALUE *rgivValues; + DWORD cValues; + + LPWSTR *rgsczLines; + DWORD cLines; + + FILE_ENCODING feEncoding; + BOOL fModified; +}; + +const int INI_HANDLE_BYTES = sizeof(INI_STRUCT); + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczOutput + ); +static void UninitializeIniValue( + INI_VALUE *pivValue + ); + +extern "C" HRESULT DAPI IniInitialize( + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle + ) +{ + HRESULT hr = S_OK; + + // Allocate the handle + *piHandle = static_cast(MemAlloc(sizeof(INI_STRUCT), TRUE)); + ExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); + +LExit: + return hr; +} + +extern "C" void DAPI IniUninitialize( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle + ) +{ + INI_STRUCT *pi = static_cast(piHandle); + + ReleaseStr(pi->sczPath); + ReleaseStr(pi->sczOpenTagPrefix); + ReleaseStr(pi->sczOpenTagPostfix); + ReleaseStr(pi->sczValuePrefix); + ReleaseStr(pi->sczValueSeparator); + + for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i) + { + ReleaseStr(pi->rgsczValueSeparatorExceptions + i); + } + + ReleaseStr(pi->sczCommentLinePrefix); + + for (DWORD i = 0; i < pi->cValues; ++i) + { + UninitializeIniValue(pi->rgivValues + i); + } + ReleaseMem(pi->rgivValues); + + ReleaseStrArray(pi->rgsczLines, pi->cLines); + + ReleaseMem(pi); +} + +extern "C" HRESULT DAPI IniSetOpenTag( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzOpenTagPrefix, + __in_z_opt LPCWSTR wzOpenTagPostfix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzOpenTagPrefix) + { + hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0); + ExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + + if (wzOpenTagPostfix) + { + hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0); + ExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzValuePrefix, + __in_z_opt LPCWSTR wzValueSeparator + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzValuePrefix) + { + hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0); + ExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); + } + else + { + ReleaseNullStr(pi->sczValuePrefix); + } + + if (wzValueSeparator) + { + hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0); + ExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); + } + else + { + ReleaseNullStr(pi->sczValueSeparator); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueSeparatorException( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z LPCWSTR wzValueNamePrefix + ) +{ + HRESULT hr = S_OK; + DWORD dwInsertedIndex = 0; + + INI_STRUCT *pi = static_cast(piHandle); + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); + dwInsertedIndex = pi->cValueSeparatorExceptions; + ++pi->cValueSeparatorExceptions; + + hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0); + ExitOnFailure(hr, "Failed to copy value separator exception"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetCommentStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzLinePrefix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzLinePrefix) + { + hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0); + ExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); + } + else + { + ReleaseNullStr(pi->sczCommentLinePrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniParse( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzPath, + __out_opt FILE_ENCODING *pfeEncodingFound + ) +{ + HRESULT hr = S_OK; + DWORD dwValuePrefixLength = 0; + DWORD dwValueSeparatorExceptionLength = 0; + LPWSTR sczContents = NULL; + LPWSTR sczCurrentSection = NULL; + LPWSTR sczName = NULL; + LPWSTR sczNameTrimmed = NULL; + LPWSTR sczValue = NULL; + LPWSTR sczValueTrimmed = NULL; + LPWSTR wzOpenTagPrefix = NULL; + LPWSTR wzOpenTagPostfix = NULL; + LPWSTR wzValuePrefix = NULL; + LPWSTR wzValueNameStart = NULL; + LPWSTR wzValueSeparator = NULL; + LPWSTR wzCommentLinePrefix = NULL; + LPWSTR wzValueBegin = NULL; + LPCWSTR wzTemp = NULL; + + INI_STRUCT *pi = static_cast(piHandle); + + BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix); + BOOL fValuePrefix = (NULL != pi->sczValuePrefix); + + hr = StrAllocString(&pi->sczPath, wzPath, 0); + ExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); + + hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding); + ExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); + + if (pfeEncodingFound) + { + *pfeEncodingFound = pi->feEncoding; + } + + if (!sczContents || !*sczContents) + { + // Empty string, nothing to parse + ExitFunction1(hr = S_OK); + } + + dwValuePrefixLength = lstrlenW(pi->sczValuePrefix); + hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast(&pi->cLines), sczContents, L"\n"); + ExitOnFailure(hr, "Failed to split INI file into lines"); + + for (DWORD i = 0; i < pi->cLines; ++i) + { + if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i]) + { + continue; + } + + if (pi->sczCommentLinePrefix) + { + wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix); + + if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1) + { + continue; + } + } + + if (pi->sczOpenTagPrefix) + { + wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix); + if (wzOpenTagPrefix) + { + // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix + // This is important, for example, to support values with names like "Array[0]=blah" in INI format + for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp) + { + if (*wzTemp != L' ' && *wzTemp != L'\t') + { + wzOpenTagPrefix = NULL; + break; + } + } + } + } + + if (pi->sczOpenTagPostfix) + { + wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix); + } + + if (pi->sczValuePrefix) + { + wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix); + if (wzValuePrefix != NULL) + { + wzValueNameStart = wzValuePrefix + dwValuePrefixLength; + } + } + else + { + wzValueNameStart = pi->rgsczLines[i]; + } + + if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0') + { + dwValueSeparatorExceptionLength = 0; + for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j) + { + if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j])) + { + dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]); + break; + } + } + + wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator); + } + + // Don't keep the endline + if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r') + { + pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0'; + } + + if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix)) + { + // There is an section starting here, let's keep track of it and move on + hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix))); + ExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); + + // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix) + && (!fValuePrefix || wzValuePrefix)) + { + if (fValuePrefix) + { + wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix); + } + else + { + wzValueBegin = pi->rgsczLines[i]; + } + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100); + ExitOnFailure(hr, "Failed to increase array size for value array"); + + if (sczCurrentSection) + { + hr = StrAllocString(&sczName, sczCurrentSection, 0); + ExitOnFailure(hr, "Failed to copy current section name"); + + hr = StrAllocConcat(&sczName, wzSectionSeparator, 0); + ExitOnFailure(hr, "Failed to copy current section name"); + } + + hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin); + ExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0); + ExitOnFailure(hr, "Failed to copy value"); + + hr = StrTrimWhitespace(&sczNameTrimmed, sczName); + ExitOnFailure(hr, "Failed to trim whitespace from name"); + + hr = StrTrimWhitespace(&sczValueTrimmed, sczValue); + ExitOnFailure(hr, "Failed to trim whitespace from value"); + + pi->rgivValues[pi->cValues].wzName = const_cast(sczNameTrimmed); + sczNameTrimmed = NULL; + pi->rgivValues[pi->cValues].wzValue = const_cast(sczValueTrimmed); + sczValueTrimmed = NULL; + pi->rgivValues[pi->cValues].dwLineNumber = i + 1; + + ++pi->cValues; + + // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else + { + // Must be a comment, so ignore it and keep it in the list to output + } + + ReleaseNullStr(sczName); + } + +LExit: + ReleaseStr(sczCurrentSection); + ReleaseStr(sczContents); + ReleaseStr(sczName); + ReleaseStr(sczNameTrimmed); + ReleaseStr(sczValue); + ReleaseStr(sczValueTrimmed); + + return hr; +} + +extern "C" HRESULT DAPI IniGetValueList( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __deref_out_ecount_opt(pcValues) INI_VALUE** prgivValues, + __out DWORD *pcValues + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + *prgivValues = pi->rgivValues; + *pcValues = pi->cValues; + + return hr; +} + +extern "C" HRESULT DAPI IniGetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + if (NULL == pValue) + { + hr = E_NOTFOUND; + ExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); + } + + if (NULL == pValue->wzValue) + { + ExitFunction1(hr = E_NOTFOUND); + } + + hr = StrAllocString(psczValue, pValue->wzValue, 0); + ExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSectionPrefix = NULL; // includes section name and backslash + LPWSTR sczName = NULL; + LPWSTR sczValue = NULL; + DWORD dwInsertIndex = DWORD_MAX; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + // We're killing the value + if (NULL == wzValue) + { + if (pValue && pValue->wzValue) + { + pi->fModified = TRUE; + sczValue = const_cast(pValue->wzValue); + pValue->wzValue = NULL; + ReleaseNullStr(sczValue); + } + + ExitFunction(); + } + else + { + if (pValue) + { + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1)) + { + pi->fModified = TRUE; + hr = StrAllocString(const_cast(&pValue->wzValue), wzValue, 0); + ExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); + } + + ExitFunction1(hr = S_OK); + } + else + { + if (wzValueName) + { + hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix); + ExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); + } + + // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in) + if (sczSectionPrefix) + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix))) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + else + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator)) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + + // Otherwise, just add it to the end + if (DWORD_MAX == dwInsertIndex) + { + dwInsertIndex = pi->cValues; + } + + pi->fModified = TRUE; + hr = MemInsertIntoArray(reinterpret_cast(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100); + ExitOnFailure(hr, "Failed to insert value into array"); + + hr = StrAllocString(&sczName, wzValueName, 0); + ExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValue, 0); + ExitOnFailure(hr, "Failed to copy value"); + + pi->rgivValues[dwInsertIndex].wzName = const_cast(sczName); + sczName = NULL; + pi->rgivValues[dwInsertIndex].wzValue = const_cast(sczValue); + sczValue = NULL; + + ++pi->cValues; + } + } + +LExit: + ReleaseStr(sczName); + ReleaseStr(sczValue); + + return hr; +} + +extern "C" HRESULT DAPI IniWriteFile( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzPath, + __in FILE_ENCODING feOverrideEncoding + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentSectionPrefix = NULL; + LPWSTR sczNewSectionPrefix = NULL; + LPWSTR sczContents = NULL; + LPCWSTR wzName = NULL; + DWORD dwLineArrayIndex = 1; + FILE_ENCODING feEncoding; + + INI_STRUCT *pi = static_cast(piHandle); + + if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding) + { + feEncoding = pi->feEncoding; + } + else + { + feEncoding = feOverrideEncoding; + } + + if (FILE_ENCODING_UNSPECIFIED == feEncoding) + { + feEncoding = FILE_ENCODING_UTF16_WITH_BOM; + } + + if (!pi->fModified) + { + ExitFunction1(hr = S_OK); + } + if (NULL == wzPath && NULL == pi->sczPath) + { + ExitFunction1(hr = E_NOTFOUND); + } + + BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix); + + hr = StrAllocString(&sczContents, L"", 0); + ExitOnFailure(hr, "Failed to begin contents string as empty string"); + + // Insert any beginning lines we didn't understand like comments + if (0 < pi->cLines) + { + while (pi->rgsczLines[dwLineArrayIndex]) + { + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0); + ExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ++dwLineArrayIndex; + } + } + + for (DWORD i = 0; i < pi->cValues; ++i) + { + // Skip if this value was killed off + if (NULL == pi->rgivValues[i].wzValue) + { + continue; + } + + // Now generate any lines for the current value like value line and maybe also a new section line before it + + // First see if we need to write a section line + hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix); + ExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); + + // If the new section prefix is different, write a section out for it + if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1))) + { + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0); + ExitOnFailure(hr, "Failed to concat open tag prefix to string"); + + // Exclude section separator (i.e. backslash) from new section prefix + hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator)); + ExitOnFailure(hr, "Failed to concat section name to string"); + + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0); + ExitOnFailure(hr, "Failed to concat open tag postfix to string"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ReleaseNullStr(sczCurrentSectionPrefix); + sczCurrentSectionPrefix = sczNewSectionPrefix; + sczNewSectionPrefix = NULL; + } + + // Inserting lines we read before the current value if appropriate + while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex) + { + // Skip any lines were purposely forgot + if (NULL == pi->rgsczLines[dwLineArrayIndex]) + { + ++dwLineArrayIndex; + continue; + } + + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0); + ExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + wzName = pi->rgivValues[i].wzName; + if (fSections) + { + wzName += lstrlenW(sczCurrentSectionPrefix); + } + + // OK, now just write the name/value pair, if it isn't deleted + if (pi->sczValuePrefix) + { + hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0); + ExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); + } + + hr = StrAllocConcat(&sczContents, wzName, 0); + ExitOnFailure(hr, "Failed to concat value name to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0); + ExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0); + ExitOnFailure(hr, "Failed to concat value to ini output buffer"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + // If no path was specified, use the path to the file we parsed + if (NULL == wzPath) + { + wzPath = pi->sczPath; + } + + hr = FileFromString(wzPath, 0, sczContents, feEncoding); + ExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); + +LExit: + ReleaseStr(sczContents); + ReleaseStr(sczCurrentSectionPrefix); + ReleaseStr(sczNewSectionPrefix); + + return hr; +} + +static void UninitializeIniValue( + INI_VALUE *pivValue + ) +{ + ReleaseStr(const_cast(pivValue->wzName)); + ReleaseStr(const_cast(pivValue->wzValue)); +} + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczOutput + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzSectionDelimiter = NULL; + + ReleaseNullStr(*psczOutput); + + wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator); + if (wzSectionDelimiter && wzSectionDelimiter != wzName) + { + hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1); + ExitOnFailure(hr, "Failed to copy section prefix"); + } + +LExit: + return hr; +} diff --git a/src/dutil/jsonutil.cpp b/src/dutil/jsonutil.cpp new file mode 100644 index 00000000..ba088705 --- /dev/null +++ b/src/dutil/jsonutil.cpp @@ -0,0 +1,672 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const DWORD JSON_STACK_INCREMENT = 5; + +// Prototypes +static HRESULT DoStart( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenStart, + __in_z LPCWSTR wzStartString + ); +static HRESULT DoEnd( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenEnd, + __in_z LPCWSTR wzEndString + ); +static HRESULT DoKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ); +static HRESULT DoValue( + __in JSON_WRITER* pWriter, + __in_z_opt LPCWSTR wzValue + ); +static HRESULT EnsureTokenStack( + __in JSON_WRITER* pWriter + ); +static HRESULT SerializeJsonString( + __out_z LPWSTR* psczJsonString, + __in_z LPCWSTR wzString + ); + + + +DAPI_(HRESULT) JsonInitializeReader( + __in_z LPCWSTR wzJson, + __in JSON_READER* pReader + ) +{ + HRESULT hr = S_OK; + + memset(pReader, 0, sizeof(JSON_READER)); + ::InitializeCriticalSection(&pReader->cs); + + hr = StrAllocString(&pReader->sczJson, wzJson, 0); + ExitOnFailure(hr, "Failed to allocate json string."); + + pReader->pwz = pReader->sczJson; + +LExit: + return hr; +} + + +DAPI_(void) JsonUninitializeReader( + __in JSON_READER* pReader + ) +{ + ReleaseStr(pReader->sczJson); + + ::DeleteCriticalSection(&pReader->cs); + memset(pReader, 0, sizeof(JSON_READER)); +} + + +static HRESULT NextToken( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken + ) +{ + HRESULT hr = S_OK; + + // Skip whitespace. + while (L' ' == *pReader->pwz || + L'\t' == *pReader->pwz || + L'\r' == *pReader->pwz || + L'\n' == *pReader->pwz || + L',' == *pReader->pwz) + { + ++pReader->pwz; + } + + switch (*pReader->pwz) + { + case L'{': + *pToken = JSON_TOKEN_OBJECT_START; + break; + + case L':': // begin object value + *pToken = JSON_TOKEN_OBJECT_VALUE; + break; + + case L'}': // end object + *pToken = JSON_TOKEN_OBJECT_END; + break; + + case L'[': // begin array + *pToken = JSON_TOKEN_ARRAY_START; + break; + + case L']': // end array + *pToken = JSON_TOKEN_ARRAY_END; + break; + + case L'"': // string + *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY; + break; + + case L't': // true + case L'f': // false + case L'n': // null + case L'-': // number + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + *pToken = JSON_TOKEN_VALUE; + break; + + case L'\0': + hr = E_NOMOREITEMS; + break; + + default: + hr = E_INVALIDSTATE; + } + + return hr; +} + + +DAPI_(HRESULT) JsonReadNext( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken, + __out JSON_VALUE* pValue + ) +{ + HRESULT hr = S_OK; + //WCHAR wz; + //JSON_TOKEN token = JSON_TOKEN_NONE; + + ::EnterCriticalSection(&pReader->cs); + + hr = NextToken(pReader, pToken); + if (E_NOMOREITEMS == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get next token."); + + if (JSON_TOKEN_VALUE == *pToken) + { + hr = JsonReadValue(pReader, pValue); + } + else + { + ++pReader->pwz; + } + +LExit: + ::LeaveCriticalSection(&pReader->cs); + return hr; +} + + +DAPI_(HRESULT) JsonReadValue( + __in JSON_READER* /*pReader*/, + __in JSON_VALUE* /*pValue*/ + ) +{ + HRESULT hr = S_OK; + +//LExit: + return hr; +} + + +DAPI_(HRESULT) JsonInitializeWriter( + __in JSON_WRITER* pWriter + ) +{ + memset(pWriter, 0, sizeof(JSON_WRITER)); + ::InitializeCriticalSection(&pWriter->cs); + + return S_OK; +} + + +DAPI_(void) JsonUninitializeWriter( + __in JSON_WRITER* pWriter + ) +{ + ReleaseMem(pWriter->rgTokenStack); + ReleaseStr(pWriter->sczJson); + + ::DeleteCriticalSection(&pWriter->cs); + memset(pWriter, 0, sizeof(JSON_WRITER)); +} + + +DAPI_(HRESULT) JsonWriteBool( + __in JSON_WRITER* pWriter, + __in BOOL fValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0); + ExitOnFailure(hr, "Failed to convert boolean to string."); + + hr = DoValue(pWriter, sczValue); + ExitOnFailure(hr, "Failed to add boolean to JSON."); + +LExit: + ReleaseStr(sczValue); + return hr; +} + + +DAPI_(HRESULT) JsonWriteNumber( + __in JSON_WRITER* pWriter, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = StrAllocFormatted(&sczValue, L"%u", dwValue); + ExitOnFailure(hr, "Failed to convert number to string."); + + hr = DoValue(pWriter, sczValue); + ExitOnFailure(hr, "Failed to add number to JSON."); + +LExit: + ReleaseStr(sczValue); + return hr; +} + + +DAPI_(HRESULT) JsonWriteString( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczJsonString = NULL; + + hr = SerializeJsonString(&sczJsonString, wzValue); + ExitOnFailure(hr, "Failed to allocate string JSON."); + + hr = DoValue(pWriter, sczJsonString); + ExitOnFailure(hr, "Failed to add string to JSON."); + +LExit: + ReleaseStr(sczJsonString); + return hr; +} + + +DAPI_(HRESULT) JsonWriteArrayStart( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"["); + ExitOnFailure(hr, "Failed to start JSON array."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteArrayEnd( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]"); + ExitOnFailure(hr, "Failed to end JSON array."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectStart( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{"); + ExitOnFailure(hr, "Failed to start JSON object."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczObjectKey = NULL; + + hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey); + ExitOnFailure(hr, "Failed to allocate JSON object key."); + + hr = DoKey(pWriter, sczObjectKey); + ExitOnFailure(hr, "Failed to add object key to JSON."); + +LExit: + ReleaseStr(sczObjectKey); + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectEnd( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}"); + ExitOnFailure(hr, "Failed to end JSON object."); + +LExit: + return hr; +} + + +static HRESULT DoStart( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenStart, + __in_z LPCWSTR wzStartString + ) +{ + Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart); + + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + BOOL fPushToken = TRUE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + ExitOnFailure(hr, "Failed to ensure token stack for start."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_NONE: + token = tokenStart; + fPushToken = FALSE; + break; + + case JSON_TOKEN_ARRAY_START: // array start changes to array value. + token = JSON_TOKEN_ARRAY_VALUE; + break; + + case JSON_TOKEN_ARRAY_VALUE: + case JSON_TOKEN_ARRAY_END: + case JSON_TOKEN_OBJECT_END: + fNeedComma = TRUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + ExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + ExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); + } + + hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); + ExitOnFailure(hr, "Failed to start JSON array or object."); + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + if (fPushToken) + { + pWriter->rgTokenStack[pWriter->cTokens] = tokenStart; + ++pWriter->cTokens; + } + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoEnd( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenEnd, + __in_z LPCWSTR wzEndString + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pWriter->cs); + + if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); + } + else + { + JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) || + (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); + } + } + + hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); + ExitOnFailure(hr, "Failed to end JSON array or object."); + + --pWriter->cTokens; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ) +{ + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + ExitOnFailure(hr, "Failed to ensure token stack for key."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_OBJECT_START: + token = JSON_TOKEN_OBJECT_KEY; + break; + + case JSON_TOKEN_OBJECT_VALUE: + token = JSON_TOKEN_OBJECT_KEY; + fNeedComma = TRUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + ExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + ExitOnFailure(hr, "Failed to add comma for key to JSON."); + } + + hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); + ExitOnFailure(hr, "Failed to add key to JSON."); + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoValue( + __in JSON_WRITER* pWriter, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + ExitOnFailure(hr, "Failed to ensure token stack for value."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_ARRAY_START: + token = JSON_TOKEN_ARRAY_VALUE; + break; + + case JSON_TOKEN_ARRAY_VALUE: + fNeedComma = TRUE; + break; + + case JSON_TOKEN_OBJECT_KEY: + token = JSON_TOKEN_OBJECT_VALUE; + break; + + case JSON_TOKEN_NONE: + token = JSON_TOKEN_VALUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + ExitOnRootFailure(hr, "Cannot add value to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + ExitOnFailure(hr, "Failed to add comma for value to JSON."); + } + + if (wzValue) + { + hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0); + ExitOnFailure(hr, "Failed to add value to JSON."); + } + else + { + hr = StrAllocConcat(&pWriter->sczJson, L"null", 0); + ExitOnFailure(hr, "Failed to add null value to JSON."); + } + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT EnsureTokenStack( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0; + + hr = MemEnsureArraySize(reinterpret_cast(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT); + ExitOnFailure(hr, "Failed to allocate JSON token stack."); + + if (0 == pWriter->cTokens) + { + pWriter->rgTokenStack[0] = JSON_TOKEN_NONE; + ++pWriter->cTokens; + } + +LExit: + return hr; +} + + +static HRESULT SerializeJsonString( + __out_z LPWSTR* psczJsonString, + __in_z LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0) + + for (LPCWSTR pch = wzString; *pch; ++pch) + { + // If it is a special JSON character, add space for the escape backslash. + if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch) + { + ++cchRequired; + } + + ++cchRequired; + } + + hr = StrAlloc(psczJsonString, cchRequired); + ExitOnFailure(hr, "Failed to allocate space for JSON string."); + + LPWSTR pchTarget = *psczJsonString; + + *pchTarget = L'\"'; + ++pchTarget; + + for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget) + { + // If it is a special JSON character, handle it or just add the character as is. + switch (*pch) + { + case L'"': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'"'; + break; + + case L'\\': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'\\'; + break; + + case L'/': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'/'; + break; + + case L'\b': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'b'; + break; + + case L'\f': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'f'; + break; + + case L'\n': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'n'; + break; + + case L'\r': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'r'; + break; + + case L'\t': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L't'; + break; + + default: + *pchTarget = *pch; + break; + } + + } + + *pchTarget = L'\"'; + ++pchTarget; + *pchTarget = L'\0'; + +LExit: + return hr; +} diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp new file mode 100644 index 00000000..b3cc042c --- /dev/null +++ b/src/dutil/locutil.cpp @@ -0,0 +1,613 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// prototypes +static HRESULT ParseWxl( + __in IXMLDOMDocument* pixd, + __out WIX_LOCALIZATION** ppWixLoc + ); +static HRESULT ParseWxlStrings( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlControls( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlString( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlControl( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ); + +// from Winnls.h +#ifndef MUI_LANGUAGE_ID +#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention +#endif +#ifndef MUI_MERGE_USER_FALLBACK +#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages +#endif +#ifndef MUI_MERGE_SYSTEM_FALLBACK +#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages +#endif +typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( + __in DWORD dwFlags, + __out PULONG pulNumLanguages, + __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, + __inout PULONG pcchLanguagesBuffer +); + +extern "C" HRESULT DAPI LocProbeForFile( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczProbePath = NULL; + LANGID langid = 0; + LPWSTR sczLangIdFile = NULL; + LPWSTR sczLangsBuff = NULL; + GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = + reinterpret_cast( + GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); + + // If a language was specified, look for a loc file in that as a directory. + if (wzLanguage && *wzLanguage) + { + hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); + ExitOnFailure(hr, "Failed to concat base path to language."); + + hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); + ExitOnFailure(hr, "Failed to concat loc file name to probe path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + if (pvfnGetThreadPreferredUILanguages) + { + ULONG nLangs; + ULONG cchLangs = 0; + DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) + { + ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); + } + + hr = StrAlloc(&sczLangsBuff, cchLangs); + ExitOnFailure(hr, "Failed to allocate buffer for languages"); + + nLangs = 0; + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) + { + ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); + } + + LPWSTR szLangs = sczLangsBuff; + for (ULONG i = 0; i < nLangs; ++i, szLangs += 5) + { + // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value. + hr = StrHexDecode(szLangs, reinterpret_cast(&langid), sizeof(langid)); + ExitOnFailure(hr, "Failed to parse langId."); + + langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid)); + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + ExitOnFailure(hr, "Failed to format user preferred langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + ExitOnFailure(hr, "Failed to concat user preferred langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + } + + langid = ::GetUserDefaultUILanguage(); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + ExitOnFailure(hr, "Failed to format user langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + ExitOnFailure(hr, "Failed to concat user langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) + { + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + ExitOnFailure(hr, "Failed to format user langid (default sublang)."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + langid = ::GetSystemDefaultUILanguage(); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + ExitOnFailure(hr, "Failed to format system langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + ExitOnFailure(hr, "Failed to concat system langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) + { + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + ExitOnFailure(hr, "Failed to format user langid (default sublang)."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + // Finally, look for the loc file in the base path. + hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); + ExitOnFailure(hr, "Failed to concat loc file name to base path."); + + if (!FileExistsEx(sczProbePath, NULL)) + { + hr = E_FILENOTFOUND; + } + +LExit: + if (SUCCEEDED(hr)) + { + hr = StrAllocString(psczPath, sczProbePath, 0); + } + + ReleaseStr(sczLangIdFile); + ReleaseStr(sczProbePath); + ReleaseStr(sczLangsBuff); + + return hr; +} + +extern "C" HRESULT DAPI LocLoadFromFile( + __in_z LPCWSTR wzWxlFile, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixd = NULL; + + hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd); + ExitOnFailure(hr, "Failed to load WXL file as XML document."); + + hr = ParseWxl(pixd, ppWixLoc); + ExitOnFailure(hr, "Failed to parse WXL."); + +LExit: + ReleaseObject(pixd); + + return hr; +} + +extern "C" HRESULT DAPI LocLoadFromResource( + __in HMODULE hModule, + __in_z LPCSTR szResource, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + LPVOID pvResource = NULL; + DWORD cbResource = 0; + LPWSTR sczXml = NULL; + IXMLDOMDocument* pixd = NULL; + + hr = ResReadData(hModule, szResource, &pvResource, &cbResource); + ExitOnFailure(hr, "Failed to read theme from resource."); + + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); + ExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + + hr = XmlLoadDocument(sczXml, &pixd); + ExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = ParseWxl(pixd, ppWixLoc); + ExitOnFailure(hr, "Failed to parse WXL."); + +LExit: + ReleaseObject(pixd); + ReleaseStr(sczXml); + + return hr; +} + +extern "C" void DAPI LocFree( + __in_opt WIX_LOCALIZATION* pWixLoc + ) +{ + if (pWixLoc) + { + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) + { + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); + } + + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) + { + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); + ReleaseStr(pWixLoc->rgLocControls[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocStrings); + ReleaseMem(pWixLoc->rgLocControls); + ReleaseMem(pWixLoc); + } +} + +extern "C" HRESULT DAPI LocLocalizeString( + __in const WIX_LOCALIZATION* pWixLoc, + __inout LPWSTR* ppsczInput + ) +{ + Assert(ppsczInput && pWixLoc); + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) + { + hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText); + ExitOnFailure(hr, "Localizing string failed."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LocGetControl( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_CONTROL** ppLocControl + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + + for (DWORD i = 0; i < pWixLoc->cLocControls; ++i) + { + pLocControl = &pWixLoc->rgLocControls[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1)) + { + *ppLocControl = pLocControl; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LocGetString( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_STRING** ppLocString + ) +{ + HRESULT hr = E_NOTFOUND; + LOC_STRING* pLocString = NULL; + + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) + { + pLocString = pWixLoc->rgLocStrings + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1)) + { + *ppLocString = pLocString; + hr = S_OK; + break; + } + } + + return hr; +} + +extern "C" HRESULT DAPI LocAddString( + __in WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzLocString, + __in BOOL bOverridable + ) +{ + HRESULT hr = S_OK; + + ++pWixLoc->cLocStrings; + pWixLoc->rgLocStrings = static_cast(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); + ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings."); + + LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1); + + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId); + ExitOnFailure(hr, "Failed to set localization string Id."); + + hr = StrAllocString(&pLocString->wzText, wzLocString, 0); + ExitOnFailure(hr, "Failed to set localization string Text."); + + pLocString->bOverridable = bOverridable; + +LExit: + return hr; +} + +// helper functions + +static HRESULT ParseWxl( + __in IXMLDOMDocument* pixd, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMElement *pWxlElement = NULL; + WIX_LOCALIZATION* pWixLoc = NULL; + + pWixLoc = static_cast(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE)); + ExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file."); + + // read the WixLocalization tag + hr = pixd->get_documentElement(&pWxlElement); + ExitOnFailure(hr, "Failed to get localization element."); + + // get the Language attribute if present + pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET; + hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId); + if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get Language value."); + + // store the strings and controls in a node list + hr = ParseWxlStrings(pWxlElement, pWixLoc); + ExitOnFailure(hr, "Parsing localization strings failed."); + + hr = ParseWxlControls(pWxlElement, pWixLoc); + ExitOnFailure(hr, "Parsing localization controls failed."); + + *ppWixLoc = pWixLoc; + pWixLoc = NULL; + +LExit: + ReleaseObject(pWxlElement); + ReleaseMem(pWixLoc); + + return hr; +} + + +static HRESULT ParseWxlStrings( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + IXMLDOMNodeList* pixnl = NULL; + DWORD dwIdx = 0; + + hr = XmlSelectNodes(pElement, L"String", &pixnl); + ExitOnLastError(hr, "Failed to get String child nodes of Wxl File."); + + hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocStrings)); + ExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File."); + + if (0 < pWixLoc->cLocStrings) + { + pWixLoc->rgLocStrings = static_cast(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); + ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = ParseWxlString(pixn, dwIdx, pWixLoc); + ExitOnFailure(hr, "Failed to parse localization string."); + + ++dwIdx; + ReleaseNullObject(pixn); + } + + hr = S_OK; + ExitOnFailure(hr, "Failed to enumerate all localization strings."); + } + +LExit: + if (FAILED(hr) && pWixLoc->rgLocStrings) + { + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) + { + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocStrings); + } + + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + +static HRESULT ParseWxlControls( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + IXMLDOMNodeList* pixnl = NULL; + DWORD dwIdx = 0; + + hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl); + ExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); + + hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocControls)); + ExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); + + if (0 < pWixLoc->cLocControls) + { + pWixLoc->rgLocControls = static_cast(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE)); + ExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = ParseWxlControl(pixn, dwIdx, pWixLoc); + ExitOnFailure(hr, "Failed to parse localized control."); + + ++dwIdx; + ReleaseNullObject(pixn); + } + + hr = S_OK; + ExitOnFailure(hr, "Failed to enumerate all localized controls."); + } + +LExit: + if (FAILED(hr) && pWixLoc->rgLocControls) + { + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) + { + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); + ReleaseStr(pWixLoc->rgLocControls[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocControls); + } + + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + +static HRESULT ParseWxlString( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_STRING* pLocString = NULL; + BSTR bstrText = NULL; + + pLocString = pWixLoc->rgLocStrings + dwIdx; + + // Id + hr = XmlGetAttribute(pixn, L"Id", &bstrText); + ExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file."); + + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText); + ExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file."); + + ReleaseNullBSTR(bstrText); + + // Overrideable + hr = XmlGetAttribute(pixn, L"Overridable", &bstrText); + ExitOnFailure(hr, "Failed to get Xml attribute Overridable."); + + if (S_OK == hr) + { + pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1); + } + + ReleaseNullBSTR(bstrText); + + // Text + hr = XmlGetText(pixn, &bstrText); + ExitOnFailure(hr, "Failed to get Xml text in Wxl file."); + + hr = StrAllocString(&pLocString->wzText, bstrText, 0); + ExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file."); + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + +static HRESULT ParseWxlControl( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + BSTR bstrText = NULL; + + pLocControl = pWixLoc->rgLocControls + dwIdx; + + // Id + hr = XmlGetAttribute(pixn, L"Control", &bstrText); + ExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file."); + + hr = StrAllocString(&pLocControl->wzControl, bstrText, 0); + ExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file."); + + ReleaseNullBSTR(bstrText); + + // X + pLocControl->nX = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pLocControl->nX)); + ExitOnFailure(hr, "Failed to get control X attribute."); + + // Y + pLocControl->nY = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pLocControl->nY)); + ExitOnFailure(hr, "Failed to get control Y attribute."); + + // Width + pLocControl->nWidth = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pLocControl->nWidth)); + ExitOnFailure(hr, "Failed to get control width attribute."); + + // Height + pLocControl->nHeight = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pLocControl->nHeight)); + ExitOnFailure(hr, "Failed to get control height attribute."); + + // Text + hr = XmlGetText(pixn, &bstrText); + ExitOnFailure(hr, "Failed to get control text in Wxl file."); + + hr = StrAllocString(&pLocControl->wzText, bstrText, 0); + ExitOnFailure(hr, "Failed to duplicate control text in Wxl file."); + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp new file mode 100644 index 00000000..c471e002 --- /dev/null +++ b/src/dutil/logutil.cpp @@ -0,0 +1,942 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// globals +static HMODULE LogUtil_hModule = NULL; +static BOOL LogUtil_fDisabled = FALSE; +static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE; +static LPWSTR LogUtil_sczLogPath = NULL; +static LPSTR LogUtil_sczPreInitBuffer = NULL; +static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD; +static CRITICAL_SECTION LogUtil_csLog = { }; +static BOOL LogUtil_fInitializedCriticalSection = FALSE; + +// Customization of certain parts of the string, within a line +static LPWSTR LogUtil_sczSpecialBeginLine = NULL; +static LPWSTR LogUtil_sczSpecialEndLine = NULL; +static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL; + +static LPCSTR LOGUTIL_UNKNOWN = "unknown"; +static LPCSTR LOGUTIL_WARNING = "warning"; +static LPCSTR LOGUTIL_STANDARD = "standard"; +static LPCSTR LOGUTIL_VERBOSE = "verbose"; +static LPCSTR LOGUTIL_DEBUG = "debug"; +static LPCSTR LOGUTIL_NONE = "none"; + +// prototypes +static HRESULT LogIdWork( + __in REPORT_LEVEL rl, + __in_opt HMODULE hModule, + __in DWORD dwLogId, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ); +static HRESULT LogStringWorkArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ); +static HRESULT LogStringWork( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_z LPCWSTR sczString, + __in BOOL fLOGUTIL_NEWLINE + ); + +// Hook to allow redirecting LogStringWorkRaw function calls +static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL; +static LPVOID s_vpvLogStringWorkRawContext = NULL; + + +/******************************************************************** + IsLogInitialized - Checks if log is currently initialized. +********************************************************************/ +extern "C" BOOL DAPI IsLogInitialized() +{ + return LogUtil_fInitializedCriticalSection; +} + +/******************************************************************** + IsLogOpen - Checks if log is currently initialized and open. +********************************************************************/ +extern "C" BOOL DAPI IsLogOpen() +{ + return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath); +} + + +/******************************************************************** + LogInitialize - initializes the logutil API + +********************************************************************/ +extern "C" void DAPI LogInitialize( + __in HMODULE hModule + ) +{ + AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called."); + + LogUtil_hModule = hModule; + LogUtil_fDisabled = FALSE; + + ::InitializeCriticalSection(&LogUtil_csLog); + LogUtil_fInitializedCriticalSection = TRUE; +} + + +/******************************************************************** + LogOpen - creates an application log file + + NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name +********************************************************************/ +extern "C" HRESULT DAPI LogOpen( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzLog, + __in_z_opt LPCWSTR wzPostfix, + __in_z_opt LPCWSTR wzExt, + __in BOOL fAppend, + __in BOOL fHeader, + __out_z_opt LPWSTR* psczLogPath + ) +{ + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + LPWSTR sczLogDirectory = NULL; + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + if (wzExt && *wzExt) + { + hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog); + ExitOnFailure(hr, "Failed to create log based on current system time."); + } + else + { + hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath); + ExitOnFailure(hr, "Failed to combine the log path."); + + hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory); + ExitOnFailure(hr, "Failed to get log directory."); + + hr = DirEnsureExists(sczLogDirectory, NULL); + ExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory); + + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + } + + if (fAppend) + { + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); + } + } + + if (fHeader) + { + LogHeader(); + } + + if (NULL != LogUtil_sczPreInitBuffer) + { + // Log anything that was logged before LogOpen() was called. + LogStringWorkRaw(LogUtil_sczPreInitBuffer); + ReleaseNullStr(LogUtil_sczPreInitBuffer); + } + + if (psczLogPath) + { + hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0); + ExitOnFailure(hr, "Failed to copy log path."); + } + + LogUtil_fDisabled = FALSE; + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + ReleaseStr(sczLogDirectory); + + return hr; +} + + +/******************************************************************** + LogDisable - closes any open files and disables in memory logging. + +********************************************************************/ +void DAPI LogDisable() +{ + ::EnterCriticalSection(&LogUtil_csLog); + + LogUtil_fDisabled = TRUE; + + ReleaseFileHandle(LogUtil_hLog); + ReleaseNullStr(LogUtil_sczLogPath); + ReleaseNullStr(LogUtil_sczPreInitBuffer); + + ::LeaveCriticalSection(&LogUtil_csLog); +} + + +/******************************************************************** + LogRedirect - Redirects all logging strings to the specified + function - or set NULL to disable the hook +********************************************************************/ +void DAPI LogRedirect( + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, + __in_opt LPVOID pvContext + ) +{ + s_vpfLogStringWorkRaw = vpfLogStringWorkRaw; + s_vpvLogStringWorkRawContext = pvContext; +} + + +/******************************************************************** + LogRename - Renames a logfile, moving its contents to a new path, + and re-opening the file for appending at the new + location +********************************************************************/ +HRESULT DAPI LogRename( + __in_z LPCWSTR wzNewPath + ) +{ + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + ReleaseFileHandle(LogUtil_hLog); + + hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE); + ExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath); + + hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0); + ExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath); + + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + } + + // Enable "append" mode by moving file pointer to the end + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + return hr; +} + + +extern "C" void DAPI LogClose( + __in BOOL fFooter + ) +{ + if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter) + { + LogFooter(); + } + + ReleaseFileHandle(LogUtil_hLog); + ReleaseNullStr(LogUtil_sczLogPath); + ReleaseNullStr(LogUtil_sczPreInitBuffer); +} + + +extern "C" void DAPI LogUninitialize( + __in BOOL fFooter + ) +{ + LogClose(fFooter); + + if (LogUtil_fInitializedCriticalSection) + { + ::DeleteCriticalSection(&LogUtil_csLog); + LogUtil_fInitializedCriticalSection = FALSE; + } + + LogUtil_hModule = NULL; + LogUtil_fDisabled = FALSE; + + ReleaseNullStr(LogUtil_sczSpecialBeginLine); + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); + ReleaseNullStr(LogUtil_sczSpecialEndLine); +} + + +/******************************************************************** + LogIsOpen - returns whether log file is open or note + +********************************************************************/ +extern "C" BOOL DAPI LogIsOpen() +{ + return INVALID_HANDLE_VALUE != LogUtil_hLog; +} + + +/******************************************************************** + LogSetSpecialParams - sets a special beginline string, endline + string, post-timestamp string, etc. +********************************************************************/ +HRESULT DAPI LogSetSpecialParams( + __in_z_opt LPCWSTR wzSpecialBeginLine, + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, + __in_z_opt LPCWSTR wzSpecialEndLine + ) +{ + HRESULT hr = S_OK; + + // Handle special string to be prepended before every full line + if (NULL == wzSpecialBeginLine) + { + ReleaseNullStr(LogUtil_sczSpecialBeginLine); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0); + ExitOnFailure(hr, "Failed to allocate copy of special beginline string"); + } + + // Handle special string to be appended to every time stamp + if (NULL == wzSpecialAfterTimeStamp) + { + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0); + ExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string"); + } + + // Handle special string to be appended before every full line + if (NULL == wzSpecialEndLine) + { + ReleaseNullStr(LogUtil_sczSpecialEndLine); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0); + ExitOnFailure(hr, "Failed to allocate copy of special endline string"); + } + +LExit: + return hr; +} + +/******************************************************************** + LogSetLevel - sets the logging level + + NOTE: returns previous logging level +********************************************************************/ +extern "C" REPORT_LEVEL DAPI LogSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fLogChange + ) +{ + AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set"); + + REPORT_LEVEL rlPrev = LogUtil_rlCurrent; + + if (LogUtil_rlCurrent != rl) + { + LogUtil_rlCurrent = rl; + + if (fLogChange) + { + LPCSTR szLevel = LOGUTIL_UNKNOWN; + switch (LogUtil_rlCurrent) + { + case REPORT_WARNING: + szLevel = LOGUTIL_WARNING; + break; + case REPORT_STANDARD: + szLevel = LOGUTIL_STANDARD; + break; + case REPORT_VERBOSE: + szLevel = LOGUTIL_VERBOSE; + break; + case REPORT_DEBUG: + szLevel = LOGUTIL_DEBUG; + break; + case REPORT_NONE: + szLevel = LOGUTIL_NONE; + break; + } + + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); + } + } + + return rlPrev; +} + + +/******************************************************************** + LogGetLevel - gets the current logging level + +********************************************************************/ +extern "C" REPORT_LEVEL DAPI LogGetLevel() +{ + return LogUtil_rlCurrent; +} + + +/******************************************************************** + LogGetPath - gets the current log path + +********************************************************************/ +extern "C" HRESULT DAPI LogGetPath( + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, + __in DWORD cchLogPath + ) +{ + Assert(pwzLogPath); + + HRESULT hr = S_OK; + + if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one! + { + ExitFunction1(hr = E_UNEXPECTED); + } + + hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath); + +LExit: + return hr; +} + + +/******************************************************************** + LogGetHandle - gets the current log file handle + +********************************************************************/ +extern "C" HANDLE DAPI LogGetHandle() +{ + return LogUtil_hLog; +} + + +/******************************************************************** + LogString - write a string to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogString( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = LogStringArgs(rl, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogStringArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogStringWorkArgs(rl, szFormat, args, FALSE); + +LExit: + return hr; +} + +/******************************************************************** + LogStringLine - write a string plus LOGUTIL_NEWLINE to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogStringLine( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = LogStringLineArgs(rl, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogStringLineArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogStringWorkArgs(rl, szFormat, args, TRUE); + +LExit: + return hr; +} + +/******************************************************************** + LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log + + NOTE: uses format string from MESSAGETABLE resource +********************************************************************/ + +extern "C" HRESULT DAPI LogIdModuleArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LogIdModule( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + ... + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + va_list args; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + va_start(args, hModule); + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); + va_end(args); + +LExit: + return hr; +} + + + + +/******************************************************************** + LogError - write an error to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogErrorString( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + + va_list args; + va_start(args, szFormat); + hr = LogErrorStringArgs(hrError, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogErrorStringArgs( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFormat = NULL; + LPWSTR sczMessage = NULL; + + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); + ExitOnFailure(hr, "Failed to convert format string to wide character string"); + + // format the string as a unicode string - this is necessary to be able to include + // international characters in our output string. This does have the counterintuitive effect + // that the caller's "%s" is interpreted differently + // (so callers should use %hs for LPSTR and %ls for LPWSTR) + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); + ExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat); + + hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage); + +LExit: + ReleaseStr(sczFormat); + ReleaseStr(sczMessage); + + return hr; +} + + +/******************************************************************** + LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log + + NOTE: uses format string from MESSAGETABLE resource + can log no more than three strings in the error message +********************************************************************/ +extern "C" HRESULT DAPI LogErrorIdModule( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzString1 = NULL, + __in_z_opt LPCWSTR wzString2 = NULL, + __in_z_opt LPCWSTR wzString3 = NULL + ) +{ + HRESULT hr = S_OK; + WCHAR wzError[11]; + WORD cStrings = 1; // guaranteed wzError is in the list + + hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError); + ExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError); + + cStrings += wzString1 ? 1 : 0; + cStrings += wzString2 ? 1 : 0; + cStrings += wzString3 ? 1 : 0; + + hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3); + ExitOnFailure(hr, "Failed to log id module."); + +LExit: + return hr; +} + +/******************************************************************** + LogHeader - write a standard header to the log + +********************************************************************/ +extern "C" HRESULT DAPI LogHeader() +{ + HRESULT hr = S_OK; + WCHAR wzComputerName[MAX_PATH]; + DWORD cchComputerName = countof(wzComputerName); + WCHAR wzPath[MAX_PATH]; + DWORD dwMajorVersion = 0; + DWORD dwMinorVersion = 0; + LPCSTR szLevel = LOGUTIL_UNKNOWN; + LPWSTR sczCurrentDateTime = NULL; + + // + // get the interesting data + // + if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath))) + { + memset(wzPath, 0, sizeof(wzPath)); + } + + hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion); + if (FAILED(hr)) + { + dwMajorVersion = 0; + dwMinorVersion = 0; + } + + if (!::GetComputerNameW(wzComputerName, &cchComputerName)) + { + ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName)); + } + + TimeCurrentDateTime(&sczCurrentDateTime, FALSE); + + // + // write data to the log + // + LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime); + LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF); + LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName); + switch (LogUtil_rlCurrent) + { + case REPORT_WARNING: + szLevel = LOGUTIL_WARNING; + break; + case REPORT_STANDARD: + szLevel = LOGUTIL_STANDARD; + break; + case REPORT_VERBOSE: + szLevel = LOGUTIL_VERBOSE; + break; + case REPORT_DEBUG: + szLevel = LOGUTIL_DEBUG; + break; + case REPORT_NONE: + szLevel = LOGUTIL_NONE; + break; + } + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); + + hr = S_OK; + + ReleaseStr(sczCurrentDateTime); + + return hr; +} + + +/******************************************************************** + LogFooterWork - write a standard footer to the log + +********************************************************************/ + +static HRESULT LogFooterWork( + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + + va_list args; + va_start(args, szFormat); + hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogFooter() +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentDateTime = NULL; + TimeCurrentDateTime(&sczCurrentDateTime, FALSE); + hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime); + ReleaseStr(sczCurrentDateTime); + return hr; +} + +/******************************************************************** + LogStringWorkRaw - Write a raw, unformatted string to the log + +********************************************************************/ +extern "C" HRESULT LogStringWorkRaw( + __in_z LPCSTR szLogData + ) +{ + Assert(szLogData && *szLogData); + + HRESULT hr = S_OK; + DWORD cbLogData = 0; + DWORD cbTotal = 0; + DWORD cbWrote = 0; + + cbLogData = lstrlenA(szLogData); + + // If the log hasn't been initialized yet, store it in a buffer + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0); + ExitOnFailure(hr, "Failed to concatenate string to pre-init buffer"); + + ExitFunction1(hr = S_OK); + } + + // write the string + while (cbTotal < cbLogData) + { + if (!::WriteFile(LogUtil_hLog, reinterpret_cast(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL)) + { + ExitOnLastError(hr, "Failed to write output to log: %ls - %ls", LogUtil_sczLogPath, szLogData); + } + + cbTotal += cbWrote; + } + +LExit: + return hr; +} + +// +// private worker functions +// +static HRESULT LogIdWork( + __in REPORT_LEVEL rl, + __in_opt HMODULE hModule, + __in DWORD dwLogId, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + DWORD cch = 0; + + // get the string for the id +#pragma prefast(push) +#pragma prefast(disable:25028) +#pragma prefast(disable:25068) + cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, + static_cast(hModule), dwLogId, 0, reinterpret_cast(&pwz), 0, &args); +#pragma prefast(pop) + + if (0 == cch) + { + ExitOnLastError(hr, "failed to log id: %d", dwLogId); + } + + if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1]) + { + pwz[cch-2] = L'\0'; // remove newline from message table + } + + LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE); + +LExit: + if (pwz) + { + ::LocalFree(pwz); + } + + return hr; +} + + +static HRESULT LogStringWorkArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + Assert(szFormat && *szFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormat = NULL; + LPWSTR sczMessage = NULL; + + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); + ExitOnFailure(hr, "Failed to convert format string to wide character string"); + + // format the string as a unicode string + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); + ExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat); + + hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE); + ExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage); + +LExit: + ReleaseStr(sczFormat); + ReleaseStr(sczMessage); + + return hr; +} + + +static HRESULT LogStringWork( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_z LPCWSTR sczString, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + Assert(sczString && *sczString); + + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + LPWSTR scz = NULL; + LPCWSTR wzLogData = NULL; + LPSTR sczMultiByte = NULL; + + // If logging is disabled, just bail. + if (LogUtil_fDisabled) + { + ExitFunction(); + } + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + if (fLOGUTIL_NEWLINE) + { + // get the process and thread id. + DWORD dwProcessId = ::GetCurrentProcessId(); + DWORD dwThreadId = ::GetCurrentThreadId(); + + // get the time relative to GMT. + SYSTEMTIME st = { }; + ::GetLocalTime(&st); + + DWORD dwId = dwLogId & 0xFFFFFFF; + DWORD dwType = dwLogId & 0xF0000000; + LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i"; + + // add line prefix and trailing newline + hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"", + dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId, + LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n"); + ExitOnFailure(hr, "Failed to format line prefix."); + } + + wzLogData = scz ? scz : sczString; + + // Convert to UTF-8 before writing out to the log file + hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to convert log string to UTF-8"); + + if (s_vpfLogStringWorkRaw) + { + hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext); + ExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString); + } + else + { + hr = LogStringWorkRaw(sczMultiByte); + ExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString); + } + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + ReleaseStr(scz); + ReleaseStr(sczMultiByte); + + return hr; +} diff --git a/src/dutil/memutil.cpp b/src/dutil/memutil.cpp new file mode 100644 index 00000000..578c65ee --- /dev/null +++ b/src/dutil/memutil.cpp @@ -0,0 +1,323 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include "precomp.h" + + +#if DEBUG +static BOOL vfMemInitialized = FALSE; +#endif + +extern "C" HRESULT DAPI MemInitialize() +{ +#if DEBUG + vfMemInitialized = TRUE; +#endif + return S_OK; +} + +extern "C" void DAPI MemUninitialize() +{ +#if DEBUG + vfMemInitialized = FALSE; +#endif +} + +extern "C" LPVOID DAPI MemAlloc( + __in SIZE_T cbSize, + __in BOOL fZero + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(0 < cbSize, "MemAlloc() called with invalid size"); + return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize); +} + + +extern "C" LPVOID DAPI MemReAlloc( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(0 < cbSize, "MemReAlloc() called with invalid size"); + return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize); +} + + +extern "C" HRESULT DAPI MemReAllocSecure( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero, + __out LPVOID* ppvNew + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer"); + AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size"); + + HRESULT hr = S_OK; + DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY; + LPVOID pvNew = NULL; + + dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0; + pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize); + if (!pvNew) + { + pvNew = MemAlloc(cbSize, fZero); + if (pvNew) + { + const SIZE_T cbCurrent = MemSize(pv); + if (-1 == cbCurrent) + { + ExitOnFailure(hr = E_INVALIDARG, "Failed to get memory size"); + } + + // HeapReAlloc may allocate more memory than requested. + const SIZE_T cbNew = MemSize(pvNew); + if (-1 == cbNew) + { + ExitOnFailure(hr = E_INVALIDARG, "Failed to get memory size"); + } + + cbSize = cbNew; + if (cbSize > cbCurrent) + { + cbSize = cbCurrent; + } + + memcpy_s(pvNew, cbNew, pv, cbSize); + + SecureZeroMemory(pv, cbCurrent); + MemFree(pv); + } + } + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory"); + + *ppvNew = pvNew; + pvNew = NULL; + +LExit: + ReleaseMem(pvNew); + + return hr; +} + + +extern "C" HRESULT DAPI MemAllocArray( + __inout LPVOID* ppvArray, + __in SIZE_T cbArrayType, + __in DWORD dwItemCount + ) +{ + return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount); +} + + +extern "C" HRESULT DAPI MemReAllocArray( + __inout LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwNewItemCount + ) +{ + HRESULT hr = S_OK; + DWORD cNew = 0; + LPVOID pvNew = NULL; + SIZE_T cbNew = 0; + + hr = ::DWordAdd(cArray, dwNewItemCount, &cNew); + ExitOnFailure(hr, "Integer overflow when calculating new element count."); + + hr = ::SIZETMult(cNew, cbArrayType, &cbNew); + ExitOnFailure(hr, "Integer overflow when calculating new block size."); + + if (*ppvArray) + { + SIZE_T cbCurrent = MemSize(*ppvArray); + if (cbCurrent < cbNew) + { + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array."); + + *ppvArray = pvNew; + } + } + else + { + pvNew = MemAlloc(cbNew, TRUE); + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + + *ppvArray = pvNew; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI MemEnsureArraySize( + __deref_out_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ) +{ + HRESULT hr = S_OK; + DWORD cNew = 0; + LPVOID pvNew = NULL; + SIZE_T cbNew = 0; + + hr = ::DWordAdd(cArray, dwGrowthCount, &cNew); + ExitOnFailure(hr, "Integer overflow when calculating new element count."); + + hr = ::SIZETMult(cNew, cbArrayType, &cbNew); + ExitOnFailure(hr, "Integer overflow when calculating new block size."); + + if (*ppvArray) + { + SIZE_T cbUsed = cArray * cbArrayType; + SIZE_T cbCurrent = MemSize(*ppvArray); + if (cbCurrent < cbUsed) + { + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger."); + + *ppvArray = pvNew; + } + } + else + { + pvNew = MemAlloc(cbNew, TRUE); + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + + *ppvArray = pvNew; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI MemInsertIntoArray( + __deref_out_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __in DWORD dwInsertIndex, + __in DWORD cInsertItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ) +{ + HRESULT hr = S_OK; + DWORD i; + BYTE *pbArray = NULL; + + if (0 == cInsertItems) + { + ExitFunction1(hr = S_OK); + } + + hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount); + ExitOnFailure(hr, "Failed to resize array while inserting items"); + + pbArray = reinterpret_cast(*ppvArray); + for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i) + { + memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType); + } + + // Zero out the newly-inserted items + memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType); + +LExit: + return hr; +} + +extern "C" void DAPI MemRemoveFromArray( + __inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID pvArray, + __in DWORD dwRemoveIndex, + __in DWORD cRemoveItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in BOOL fPreserveOrder + ) +{ + BYTE *pbArray = static_cast(pvArray); + DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex); + + if (fPreserveOrder) + { + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType); + } + else + { + DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems); + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType); + } + + ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType); +} + +extern "C" void DAPI MemArraySwapItems( + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __in DWORD dwIndex1, + __in DWORD dwIndex2, + __in SIZE_T cbArrayType + ) +{ + BYTE *pbArrayItem1 = static_cast(pvArray) + dwIndex1 * cbArrayType; + BYTE *pbArrayItem2 = static_cast(pvArray) + dwIndex2 * cbArrayType; + DWORD dwByteIndex = 0; + + if (dwIndex1 == dwIndex2) + { + return; + } + + // Use XOR swapping to avoid the need for a temporary item + while (dwByteIndex < cbArrayType) + { + // Try to do many bytes at a time in most cases + if (cbArrayType - dwByteIndex > sizeof(DWORD64)) + { + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // y: X xor Y + *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + + dwByteIndex += sizeof(DWORD64); + } + else + { + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // y: X xor Y + *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + + dwByteIndex += sizeof(unsigned char); + } + } +} + +extern "C" HRESULT DAPI MemFree( + __in LPVOID pv + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()); +} + + +extern "C" SIZE_T DAPI MemSize( + __in LPCVOID pv + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + return ::HeapSize(::GetProcessHeap(), 0, pv); +} diff --git a/src/dutil/metautil.cpp b/src/dutil/metautil.cpp new file mode 100644 index 00000000..612b1127 --- /dev/null +++ b/src/dutil/metautil.cpp @@ -0,0 +1,356 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// okay, this may look a little weird, but metautil.h cannot be in the +// pre-compiled header because we need to #define these things so the +// correct GUID's get pulled into this object file +#include +#include "metautil.h" + + +// prototypes +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ); + + +/******************************************************************** + MetaFindWebBase - finds a metabase base string that matches IP, Port and Header + +********************************************************************/ +extern "C" HRESULT DAPI MetaFindWebBase( + __in IMSAdminBaseW* piMetabase, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase && cchWebBase); + + HRESULT hr = S_OK; + + BOOL fFound = FALSE; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + DWORD dwIndex = 0; + + METADATA_RECORD mr; + METADATA_RECORD mrAddress; + + LPWSTR pwzExists = NULL; + LPWSTR pwzIPExists = NULL; + LPWSTR pwzPortExists = NULL; + int iPortExists = 0; + LPCWSTR pwzHeaderExists = NULL; + + memset(&mr, 0, sizeof(mr)); + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = METADATA_INHERIT; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + + memset(&mrAddress, 0, sizeof(mrAddress)); + mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS; + mrAddress.dwMDAttributes = METADATA_INHERIT; + mrAddress.dwMDUserType = IIS_MD_UT_SERVER; + mrAddress.dwMDDataType = ALL_METADATA; + + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (FAILED(hr)) + break; + + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + ExitOnFailure(hr, "failed to get key from metabase while searching for web servers"); + + // if we have an IIsWebServer store the key + if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) + { + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress); + if (MD_ERROR_DATA_NOT_FOUND == hr) + hr = S_FALSE; + ExitOnFailure(hr, "failed to get address from metabase while searching for web servers"); + + // break down the first address into parts + pwzIPExists = reinterpret_cast(mrAddress.pbMDData); + pwzExists = wcsstr(pwzIPExists, L":"); + if (NULL == pwzExists) + continue; + + *pwzExists = L'\0'; + + pwzPortExists = pwzExists + 1; + pwzExists = wcsstr(pwzPortExists, L":"); + if (NULL == pwzExists) + continue; + + *pwzExists = L'\0'; + iPortExists = wcstol(pwzPortExists, NULL, 10); + + pwzHeaderExists = pwzExists + 1; + + // compare the passed in address with the address listed for this web + if (S_OK == hr && + (0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) && + iPort == iPortExists && + 0 == lstrcmpW(wzHeader, pwzHeaderExists)) + { + // if the passed in buffer wasn't big enough + hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey); + ExitOnFailure(hr, "failed to copy in web base: %ls", wzKey); + + fFound = TRUE; + break; + } + } + } + + if (E_NOMOREITEMS == hr) + { + Assert(!fFound); + hr = S_FALSE; + } + +LExit: + MetaFreeValue(&mrAddress); + MetaFreeValue(&mr); + + if (!fFound && SUCCEEDED(hr)) + hr = S_FALSE; + + return hr; +} + + +/******************************************************************** + MetaFindFreeWebBase - finds the next metabase base string + +********************************************************************/ +extern "C" HRESULT DAPI MetaFindFreeWebBase( + __in IMSAdminBaseW* piMetabase, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + DWORD dwSubKeys[100]; + int cSubKeys = 0; + DWORD dwIndex = 0; + + int i; + DWORD dwKey; + + METADATA_RECORD mr; + + memset(&mr, 0, sizeof(mr)); + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = 0; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = STRING_METADATA; + + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (FAILED(hr)) + break; + + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + ExitOnFailure(hr, "failed to get key from metabase while searching for free web root"); + + // if we have a IIsWebServer get the address information + if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast(mr.pbMDData))) + { + if (cSubKeys >= countof(dwSubKeys)) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites"); + } + + dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10); + ++cSubKeys; + Sort(dwSubKeys, cSubKeys); + } + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + ExitOnFailure(hr, "failed to find free web root"); + + // find the lowest free web root + dwKey = 1; + for (i = 0; i < cSubKeys; ++i) + { + if (dwKey < dwSubKeys[i]) + break; + + dwKey = dwSubKeys[i] + 1; + } + + hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey); +LExit: + MetaFreeValue(&mr); + return hr; +} + + +/******************************************************************** + MetaOpenKey - open key + +********************************************************************/ +extern "C" HRESULT DAPI MetaOpenKey( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __in DWORD dwAccess, + __in DWORD cRetries, + __out METADATA_HANDLE* pmh + ) +{ + Assert(piMetabase && pmh); + + HRESULT hr = S_OK; + + // loop while the key is busy + do + { + hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh); + if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr) + ::SleepEx(1000, TRUE); + } while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--); + + return hr; +} + + +/******************************************************************** + MetaGetValue - finds the next metabase base string + + NOTE: piMetabase is optional +********************************************************************/ +extern "C" HRESULT DAPI MetaGetValue( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __inout METADATA_RECORD* pmr + ) +{ + Assert(pmr); + + HRESULT hr = S_OK; + BOOL fInitialized = FALSE; + DWORD cbRequired = 0; + + if (!piMetabase) + { + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitialized = TRUE; + + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + ExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object"); + } + + if (!pmr->pbMDData) + { + pmr->dwMDDataLen = 256; + pmr->pbMDData = static_cast(MemAlloc(pmr->dwMDDataLen, TRUE)); + ExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); + } + else // set the size of the data to the actual size of the memory + pmr->dwMDDataLen = (DWORD)MemSize(pmr->pbMDData); + + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); + if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) + { + pmr->dwMDDataLen = cbRequired; + BYTE* pb = static_cast(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE)); + ExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value"); + + pmr->pbMDData = pb; + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); + } + ExitOnFailure(hr, "failed to get metabase data"); + +LExit: + if (fInitialized) + { + ReleaseObject(piMetabase); + ::CoUninitialize(); + } + + return hr; +} + + +/******************************************************************** + MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue() + + NOTE: METADATA_RECORD must have been returned from MetaGetValue() above +********************************************************************/ +extern "C" void DAPI MetaFreeValue( + __in METADATA_RECORD* pmr + ) +{ + Assert(pmr); + + ReleaseNullMem(pmr->pbMDData); +} + + +// +// private +// + +/******************************************************************** + Sort - quick and dirty insertion sort + +********************************************************************/ +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ) +{ + int i, j; + DWORD dwData; + + for (i = 1; i < cArray; ++i) + { + dwData = dwArray[i]; + + j = i - 1; + while (0 <= j && dwArray[j] > dwData) + { + dwArray[j + 1] = dwArray[j]; + j--; + } + + dwArray[j + 1] = dwData; + } +} diff --git a/src/dutil/monutil.cpp b/src/dutil/monutil.cpp new file mode 100644 index 00000000..6f280538 --- /dev/null +++ b/src/dutil/monutil.cpp @@ -0,0 +1,2004 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const int MON_THREAD_GROWTH = 5; +const int MON_ARRAY_GROWTH = 40; +const int MON_MAX_MONITORS_PER_THREAD = 63; +const int MON_THREAD_INIT_RETRIES = 1000; +const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10; +const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute +const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently +const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000; +const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass"; + +enum MON_MESSAGE +{ + MON_MESSAGE_ADD = WM_APP + 1, + MON_MESSAGE_REMOVE, + MON_MESSAGE_REMOVED, // Sent by waiter thread back to coordinator thread to indicate a remove occurred + 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). + 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. + MON_MESSAGE_NETWORK_STATUS_UPDATE, // Some change to network connectivity occurred (a network connection was connected or disconnected for example) + MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any successful network waits. + // Annoyingly, this is necessary to catch the rare case that the remote server goes offline unexpectedly, such as by + // network cable unplugged or power loss - in this case there is no local network status change, and the wait will just never fire. + // So we very occasionally retry all successful network waits. When this occurs, we notify for changes, even though there may not have been any. + // 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. + // 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). + MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any failed network waits + MON_MESSAGE_DRIVE_STATUS_UPDATE, // Some change to local drive has occurred (new drive created or plugged in, or removed) + MON_MESSAGE_DRIVE_QUERY_REMOVE, // User wants to unplug a drive, which MonUtil will always allow + MON_MESSAGE_STOP +}; + +enum MON_TYPE +{ + MON_NONE = 0, + MON_DIRECTORY = 1, + MON_REGKEY = 2 +}; + +struct MON_REQUEST +{ + MON_TYPE type; + DWORD dwMaxSilencePeriodInMs; + + // Handle to the main window for RegisterDeviceNotification() (same handle as owned by coordinator thread) + HWND hwnd; + // and handle to the notification (specific to this request) + HDEVNOTIFY hNotify; + + BOOL fRecursive; + void *pvContext; + + HRESULT hrStatus; + + LPWSTR sczOriginalPathRequest; + BOOL fNetwork; // This reflects either a UNC or mounted drive original request + DWORD dwPathHierarchyIndex; + LPWSTR *rgsczPathHierarchy; + DWORD cPathHierarchy; + + // 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 + // after notification, we set fPendingFire back to FALSE + BOOL fPendingFire; + BOOL fSkipDeltaAdd; + DWORD dwSilencePeriodInMs; + + union + { + struct + { + } directory; + struct + { + HKEY hkRoot; + HKEY hkSubKey; + REG_KEY_BITNESS kbKeyBitness; // Only used to pass on 32-bit, 64-bit, or default parameter + } regkey; + }; +}; + +struct MON_ADD_MESSAGE +{ + MON_REQUEST request; + HANDLE handle; +}; + +struct MON_REMOVE_MESSAGE +{ + MON_TYPE type; + BOOL fRecursive; + + union + { + struct + { + LPWSTR sczDirectory; + } directory; + struct + { + HKEY hkRoot; + LPWSTR sczSubKey; + REG_KEY_BITNESS kbKeyBitness; + } regkey; + }; +}; + +struct MON_WAITER_CONTEXT +{ + DWORD dwCoordinatorThreadId; + + HANDLE hWaiterThread; + DWORD dwWaiterThreadId; + BOOL fWaiterThreadMessageQueueInitialized; + + // Callbacks + PFN_MONGENERAL vpfMonGeneral; + PFN_MONDIRECTORY vpfMonDirectory; + PFN_MONREGKEY vpfMonRegKey; + + // Context for callbacks + LPVOID pvContext; + + // HANDLEs are in their own array for easy use with WaitForMultipleObjects() + // After initialization, the very first handle is just to wake the listener thread to have it re-wait on a new list + // 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 + HANDLE *rgHandles; + DWORD cHandles; + + // Requested things to monitor + MON_REQUEST *rgRequests; + DWORD cRequests; + + // Number of pending notifications + DWORD cRequestsPending; + + // Number of requests in a failed state (couldn't initiate wait) + DWORD cRequestsFailing; +}; + +// Info stored about each waiter by the coordinator +struct MON_WAITER_INFO +{ + DWORD cMonitorCount; + + MON_WAITER_CONTEXT *pWaiterContext; +}; + +// This struct is used when Thread A wants to send a task to another thread B (and get notified when the task finishes) +// 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 +// (even long after sender is no longer waiting, in case thread has huge message queue) +// and you must send 2 parameters in the message: +// 1) a pointer to this struct (which is always valid) +// 2) the original value of dwIteration +// The receiver of the message can compare the current value of dwSendIteration in the struct with what was sent in the message +// If values are different, we're too late and thread A is no longer waiting on this response +// otherwise, set dwResponseIteration to the same value, and call ::SetEvent() on hWait +// Thread A will then wakeup, and must verify that dwResponseIteration == dwSendIteration to ensure it isn't an earlier out-of-date reply +// replying to a newer wait +// pvContext is used to send a misc parameter related to processing data +struct MON_INTERNAL_TEMPORARY_WAIT +{ + // Should be incremented each time sender sends a pointer to this struct, so each request has a different iteration + DWORD dwSendIteration; + DWORD dwReceiveIteration; + HANDLE hWait; + void *pvContext; +}; + +struct MON_STRUCT +{ + HANDLE hCoordinatorThread; + DWORD dwCoordinatorThreadId; + BOOL fCoordinatorThreadMessageQueueInitialized; + + // Invisible window for receiving network status & drive added/removal messages + HWND hwnd; + // Used by window procedure for sending request and waiting for response from waiter threads + // such as in event of a request to remove a device + MON_INTERNAL_TEMPORARY_WAIT internalWait; + + // Callbacks + PFN_MONGENERAL vpfMonGeneral; + PFN_MONDRIVESTATUS vpfMonDriveStatus; + PFN_MONDIRECTORY vpfMonDirectory; + PFN_MONREGKEY vpfMonRegKey; + + // Context for callbacks + LPVOID pvContext; + + // Waiter thread array + MON_WAITER_INFO *rgWaiterThreads; + DWORD cWaiterThreads; +}; + +const int MON_HANDLE_BYTES = sizeof(MON_STRUCT); + +static DWORD WINAPI CoordinatorThread( + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext + ); +// Initiates (or if *pHandle is non-null, continues) wait on the directory or subkey +// if the directory or subkey doesn't exist, instead calls it on the first existing parent directory or subkey +// writes to pRequest->dwPathHierarchyIndex with the array index that was waited on +static HRESULT InitiateWait( + __inout MON_REQUEST *pRequest, + __inout HANDLE *pHandle + ); +static DWORD WINAPI WaiterThread( + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext + ); +static void Notify( + __in HRESULT hr, + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REQUEST *pRequest + ); +static void MonRequestDestroy( + __in MON_REQUEST *pRequest + ); +static void MonAddMessageDestroy( + __in MON_ADD_MESSAGE *pMessage + ); +static void MonRemoveMessageDestroy( + __in MON_REMOVE_MESSAGE *pMessage + ); +static BOOL GetRecursiveFlag( + __in MON_REQUEST *pRequest, + __in DWORD dwIndex + ); +static HRESULT FindRequestIndex( + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REMOVE_MESSAGE *pMessage, + __out DWORD *pdwIndex + ); +static HRESULT RemoveRequest( + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex + ); +static REGSAM GetRegKeyBitness( + __in MON_REQUEST *pRequest + ); +static HRESULT DuplicateRemoveMessage( + __in MON_REMOVE_MESSAGE *pMessage, + __out MON_REMOVE_MESSAGE **ppMessage + ); +static LRESULT CALLBACK MonWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static HRESULT CreateMonWindow( + __in MON_STRUCT *pm, + __out HWND *pHwnd + ); +// if *phMonitor is non-NULL, closes the old wait before re-starting the new wait +static HRESULT WaitForNetworkChanges( + __inout HANDLE *phMonitor, + __in MON_STRUCT *pm + ); +static HRESULT UpdateWaitStatus( + __in HRESULT hrNewStatus, + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex, + __out DWORD *pdwNewRequestIndex + ); + +extern "C" HRESULT DAPI MonCreate( + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, + __in PFN_MONGENERAL vpfMonGeneral, + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, + __in_opt PFN_MONDIRECTORY vpfMonDirectory, + __in_opt PFN_MONREGKEY vpfMonRegKey, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD dwRetries = MON_THREAD_INIT_RETRIES; + + ExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor"); + + // Allocate the struct + *pHandle = static_cast(MemAlloc(sizeof(MON_STRUCT), TRUE)); + ExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object"); + + MON_STRUCT *pm = static_cast(*pHandle); + + pm->vpfMonGeneral = vpfMonGeneral; + pm->vpfMonDriveStatus = vpfMonDriveStatus; + pm->vpfMonDirectory = vpfMonDirectory; + pm->vpfMonRegKey = vpfMonRegKey; + pm->pvContext = pvContext; + + pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId); + if (!pm->hCoordinatorThread) + { + ExitWithLastError(hr, "Failed to create waiter thread."); + } + + // 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. + while (!pm->fCoordinatorThreadMessageQueueInitialized && 0 < dwRetries) + { + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); + --dwRetries; + } + + if (0 == dwRetries) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI MonAddDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzDirectory, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvDirectoryContext + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczDirectory = NULL; + LPWSTR sczOriginalPathRequest = NULL; + MON_ADD_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0); + ExitOnFailure(hr, "Failed to convert directory string to UNC path"); + + hr = PathBackslashTerminate(&sczOriginalPathRequest); + ExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\') + { + pMessage->request.fNetwork = TRUE; + } + else + { + hr = UncConvertFromMountedDrive(&sczDirectory, sczOriginalPathRequest); + if (SUCCEEDED(hr)) + { + pMessage->request.fNetwork = TRUE; + } + } + + if (NULL == sczDirectory) + { + // Likely not a mounted drive - just copy the request then + hr = S_OK; + + hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0); + ExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest); + } + + pMessage->handle = INVALID_HANDLE_VALUE; + pMessage->request.type = MON_DIRECTORY; + pMessage->request.fRecursive = fRecursive; + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs; + pMessage->request.hwnd = pm->hwnd; + pMessage->request.pvContext = pvDirectoryContext; + pMessage->request.sczOriginalPathRequest = sczOriginalPathRequest; + sczOriginalPathRequest = NULL; + + hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); + ExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory); + + if (0 < pMessage->request.cPathHierarchy) + { + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) + { + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + } + pMessage = NULL; + } + +LExit: + ReleaseStr(sczDirectory); + ReleaseStr(sczOriginalPathRequest); + MonAddMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonAddRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvRegKeyContext + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczSubKey = NULL; + MON_ADD_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczSubKey, wzSubKey, 0); + ExitOnFailure(hr, "Failed to copy subkey string"); + + hr = PathBackslashTerminate(&sczSubKey); + ExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor"); + + pMessage->request.type = MON_REGKEY; + pMessage->request.regkey.hkRoot = hkRoot; + pMessage->request.regkey.kbKeyBitness = kbKeyBitness; + pMessage->request.fRecursive = fRecursive; + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs, + pMessage->request.hwnd = pm->hwnd; + pMessage->request.pvContext = pvRegKeyContext; + + hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); + ExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey); + + if (0 < pMessage->request.cPathHierarchy) + { + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); + ExitOnFailure(hr, "Failed to initiate wait"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) + { + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey); + } + pMessage = NULL; + } + +LExit: + ReleaseStr(sczSubKey); + MonAddMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonRemoveDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzDirectory, + __in BOOL fRecursive + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczDirectory = NULL; + MON_REMOVE_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczDirectory, wzDirectory, 0); + ExitOnFailure(hr, "Failed to copy directory string"); + + hr = PathBackslashTerminate(&sczDirectory); + ExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->type = MON_DIRECTORY; + pMessage->fRecursive = fRecursive; + + hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0); + ExitOnFailure(hr, "Failed to allocate copy of directory string"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) + { + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + } + pMessage = NULL; + +LExit: + MonRemoveMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonRemoveRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczSubKey = NULL; + MON_REMOVE_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczSubKey, wzSubKey, 0); + ExitOnFailure(hr, "Failed to copy subkey string"); + + hr = PathBackslashTerminate(&sczSubKey); + ExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->type = MON_REGKEY; + pMessage->regkey.hkRoot = hkRoot; + pMessage->regkey.kbKeyBitness = kbKeyBitness; + pMessage->fRecursive = fRecursive; + + hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0); + ExitOnFailure(hr, "Failed to allocate copy of directory string"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) + { + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey); + } + pMessage = NULL; + +LExit: + ReleaseStr(sczSubKey); + MonRemoveMessageDestroy(pMessage); + + return hr; +} + +extern "C" void DAPI MonDestroy( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + MON_STRUCT *pm = static_cast(handle); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) + { + er = ::GetLastError(); + if (ERROR_INVALID_THREAD_ID == er) + { + // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to send message to background thread to halt"); + } + + if (pm->hCoordinatorThread) + { + ::WaitForSingleObject(pm->hCoordinatorThread, INFINITE); + ::CloseHandle(pm->hCoordinatorThread); + } + +LExit: + return; +} + +static void MonRequestDestroy( + __in MON_REQUEST *pRequest + ) +{ + if (NULL != pRequest) + { + if (MON_REGKEY == pRequest->type) + { + ReleaseRegKey(pRequest->regkey.hkSubKey); + } + else if (MON_DIRECTORY == pRequest->type && pRequest->hNotify) + { + UnregisterDeviceNotification(pRequest->hNotify); + pRequest->hNotify = NULL; + } + ReleaseStr(pRequest->sczOriginalPathRequest); + ReleaseStrArray(pRequest->rgsczPathHierarchy, pRequest->cPathHierarchy); + } +} + +static void MonAddMessageDestroy( + __in MON_ADD_MESSAGE *pMessage + ) +{ + if (NULL != pMessage) + { + MonRequestDestroy(&pMessage->request); + if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle) + { + ::FindCloseChangeNotification(pMessage->handle); + } + else if (MON_REGKEY == pMessage->request.type) + { + ReleaseHandle(pMessage->handle); + } + + ReleaseMem(pMessage); + } +} + +static void MonRemoveMessageDestroy( + __in MON_REMOVE_MESSAGE *pMessage + ) +{ + if (NULL != pMessage) + { + switch (pMessage->type) + { + case MON_DIRECTORY: + ReleaseStr(pMessage->directory.sczDirectory); + break; + case MON_REGKEY: + ReleaseStr(pMessage->regkey.sczSubKey); + break; + default: + Assert(false); + } + + ReleaseMem(pMessage); + } +} + +static DWORD WINAPI CoordinatorThread( + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + MSG msg = { }; + DWORD dwThreadIndex = DWORD_MAX; + DWORD dwRetries; + DWORD dwFailingNetworkWaits = 0; + MON_WAITER_CONTEXT *pWaiterContext = NULL; + MON_REMOVE_MESSAGE *pRemoveMessage = NULL; + MON_REMOVE_MESSAGE *pTempRemoveMessage = NULL; + MON_STRUCT *pm = reinterpret_cast(pvContext); + WSADATA wsaData = { }; + HANDLE hMonitor = NULL; + BOOL fRet = FALSE; + UINT_PTR uTimerSuccessfulNetworkRetry = 0; + UINT_PTR uTimerFailedNetworkRetry = 0; + + // Ensure the thread has a message queue + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + pm->fCoordinatorThreadMessageQueueInitialized = TRUE; + + hr = CreateMonWindow(pm, &pm->hwnd); + ExitOnFailure(hr, "Failed to create window for status update thread"); + + ::WSAStartup(MAKEWORD(2, 2), &wsaData); + + hr = WaitForNetworkChanges(&hMonitor, pm); + ExitOnFailure(hr, "Failed to wait for network changes"); + + uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL); + if (0 == uTimerSuccessfulNetworkRetry) + { + ExitWithLastError(hr, "Failed to set timer for network successful retry"); + } + + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Unexpected return value from message pump."); + } + else + { + switch (msg.message) + { + case MON_MESSAGE_ADD: + dwThreadIndex = DWORD_MAX; + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + if (pm->rgWaiterThreads[i].cMonitorCount < MON_MAX_MONITORS_PER_THREAD) + { + dwThreadIndex = i; + break; + } + } + + if (dwThreadIndex < pm->cWaiterThreads) + { + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; + } + else + { + hr = MemEnsureArraySize(reinterpret_cast(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH); + ExitOnFailure(hr, "Failed to grow waiter thread array size"); + ++pm->cWaiterThreads; + + dwThreadIndex = pm->cWaiterThreads - 1; + pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE)); + ExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct"); + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; + pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId(); + pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral; + pWaiterContext->vpfMonDirectory = pm->vpfMonDirectory; + pWaiterContext->vpfMonRegKey = pm->vpfMonRegKey; + pWaiterContext->pvContext = pm->pvContext; + + hr = MemEnsureArraySize(reinterpret_cast(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0); + ExitOnFailure(hr, "Failed to allocate first handle"); + pWaiterContext->cHandles = 1; + + pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL); + ExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event"); + + pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId); + if (!pWaiterContext->hWaiterThread) + { + ExitWithLastError(hr, "Failed to create waiter thread."); + } + + dwRetries = MON_THREAD_INIT_RETRIES; + while (!pWaiterContext->fWaiterThreadMessageQueueInitialized && 0 < dwRetries) + { + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); + --dwRetries; + } + + if (0 == dwRetries) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + } + } + + ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount; + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0)) + { + ExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message"); + } + break; + + case MON_MESSAGE_REMOVE: + // Send remove to all waiter threads. They'll ignore it if they don't have that monitor. + // If they do have that monitor, they'll remove it from their list, and tell coordinator they have another + // empty slot via MON_MESSAGE_REMOVED message + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + pRemoveMessage = reinterpret_cast(msg.wParam); + + hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage); + ExitOnFailure(hr, "Failed to duplicate remove message"); + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pTempRemoveMessage), msg.lParam)) + { + ExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + } + pTempRemoveMessage = NULL; + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message"); + } + } + MonRemoveMessageDestroy(pRemoveMessage); + pRemoveMessage = NULL; + break; + + case MON_MESSAGE_REMOVED: + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + if (pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId == static_cast(msg.wParam)) + { + Assert(pm->rgWaiterThreads[i].cMonitorCount > 0); + --pm->rgWaiterThreads[i].cMonitorCount; + if (0 == pm->rgWaiterThreads[i].cMonitorCount) + { + if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) + { + ExitWithLastError(hr, "Failed to send message to waiter thread to stop"); + } + MemRemoveFromArray(reinterpret_cast(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE); + --pm->cWaiterThreads; + --i; // reprocess this index in the for loop, which will now contain the item after the one we removed + } + } + } + break; + + case MON_MESSAGE_NETWORK_WAIT_FAILED: + if (0 == dwFailingNetworkWaits) + { + uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL); + if (0 == uTimerFailedNetworkRetry) + { + ExitWithLastError(hr, "Failed to set timer for network fail retry"); + } + } + ++dwFailingNetworkWaits; + break; + + case MON_MESSAGE_NETWORK_WAIT_SUCCEEDED: + --dwFailingNetworkWaits; + if (0 == dwFailingNetworkWaits) + { + if (!::KillTimer(NULL, uTimerFailedNetworkRetry)) + { + ExitWithLastError(hr, "Failed to kill timer for network fail retry"); + } + uTimerFailedNetworkRetry = 0; + } + break; + + case MON_MESSAGE_NETWORK_STATUS_UPDATE: + hr = WaitForNetworkChanges(&hMonitor, pm); + ExitOnFailure(hr, "Failed to re-wait for network changes"); + + // Propagate any network status update messages to all waiter threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0)) + { + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + } + } + break; + + case WM_TIMER: + // Timer means some network wait is failing, and we need to retry every so often in case a remote server goes back up + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0)) + { + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + } + } + break; + + case MON_MESSAGE_DRIVE_STATUS_UPDATE: + // If user requested to be notified of drive status updates, notify! + if (pm->vpfMonDriveStatus) + { + pm->vpfMonDriveStatus(static_cast(msg.wParam), static_cast(msg.lParam), pm->pvContext); + } + + // Propagate any drive status update messages to all waiter threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam)) + { + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message"); + } + } + break; + + case MON_MESSAGE_STOP: + ExitFunction1(hr = static_cast(msg.wParam)); + + default: + // This thread owns a window, so this handles all the other random messages we get + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + break; + } + } + } + +LExit: + if (uTimerFailedNetworkRetry) + { + fRet = ::KillTimer(NULL, uTimerFailedNetworkRetry); + } + if (uTimerSuccessfulNetworkRetry) + { + fRet = ::KillTimer(NULL, uTimerSuccessfulNetworkRetry); + } + + if (pm->hwnd) + { + ::CloseWindow(pm->hwnd); + } + + // Tell all waiter threads to shutdown + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + if (NULL != pWaiterContext->rgHandles[0]) + { + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to waiter thread to stop"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify waiter thread of incoming message"); + } + } + } + + if (hMonitor != NULL) + { + ::WSALookupServiceEnd(hMonitor); + } + + // Now confirm they're actually shut down before returning + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + if (NULL != pWaiterContext->hWaiterThread) + { + ::WaitForSingleObject(pWaiterContext->hWaiterThread, INFINITE); + ::CloseHandle(pWaiterContext->hWaiterThread); + } + + // Waiter thread can't release these, because coordinator thread uses it to try communicating with waiter thread + ReleaseHandle(pWaiterContext->rgHandles[0]); + ReleaseMem(pWaiterContext->rgHandles); + + ReleaseMem(pWaiterContext); + } + + if (FAILED(hr)) + { + // If coordinator thread fails, notify general callback of an error + Assert(pm->vpfMonGeneral); + pm->vpfMonGeneral(hr, pm->pvContext); + } + MonRemoveMessageDestroy(pRemoveMessage); + MonRemoveMessageDestroy(pTempRemoveMessage); + + ::WSACleanup(); + + return hr; +} + +static HRESULT InitiateWait( + __inout MON_REQUEST *pRequest, + __inout HANDLE *pHandle + ) +{ + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + DEV_BROADCAST_HANDLE dev = { }; + BOOL fRedo = FALSE; + BOOL fHandleFound; + DWORD er = ERROR_SUCCESS; + DWORD dwIndex = 0; + HKEY hk = NULL; + HANDLE hTemp = INVALID_HANDLE_VALUE; + + if (pRequest->hNotify) + { + UnregisterDeviceNotification(pRequest->hNotify); + pRequest->hNotify = NULL; + } + + do + { + fRedo = FALSE; + fHandleFound = FALSE; + + for (DWORD i = 0; i < pRequest->cPathHierarchy && !fHandleFound; ++i) + { + dwIndex = pRequest->cPathHierarchy - i - 1; + switch (pRequest->type) + { + case MON_DIRECTORY: + if (INVALID_HANDLE_VALUE != *pHandle) + { + ::FindCloseChangeNotification(*pHandle); + *pHandle = INVALID_HANDLE_VALUE; + } + + *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); + if (INVALID_HANDLE_VALUE == *pHandle) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || E_ACCESSDENIED == hr) + { + continue; + } + ExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]); + } + else + { + fHandleFound = TRUE; + hr = S_OK; + } + break; + case MON_REGKEY: + ReleaseRegKey(pRequest->regkey.hkSubKey); + hr = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex], KEY_NOTIFY | GetRegKeyBitness(pRequest), &pRequest->regkey.hkSubKey); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + continue; + } + ExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + + er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE); + ReleaseRegKey(hk); + hr = HRESULT_FROM_WIN32(er); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || HRESULT_FROM_WIN32(ERROR_KEY_DELETED) == hr) + { + continue; + } + else + { + ExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + + fHandleFound = TRUE; + } + + break; + default: + return E_INVALIDARG; + } + } + + pRequest->dwPathHierarchyIndex = dwIndex; + + // 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. + // If it has, restart the whole loop + if (dwIndex < pRequest->cPathHierarchy - 1) + { + switch (pRequest->type) + { + case MON_DIRECTORY: + 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); + if (INVALID_HANDLE_VALUE != hTemp) + { + ::FindCloseChangeNotification(hTemp); + fRedo = TRUE; + } + break; + case MON_REGKEY: + hrTemp = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex + 1], KEY_NOTIFY | GetRegKeyBitness(pRequest), &hk); + ReleaseRegKey(hk); + fRedo = SUCCEEDED(hrTemp); + break; + default: + Assert(false); + } + } + } while (fRedo); + + ExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]); + + if (MON_DIRECTORY == pRequest->type) + { + dev.dbch_size = sizeof(dev); + dev.dbch_devicetype = DBT_DEVTYP_HANDLE; + dev.dbch_handle = *pHandle; + // Ignore failure on this - some drives by design don't support it (like network paths), and the worst that can happen is a + // removable device will be left in use so user cannot gracefully remove + pRequest->hNotify = RegisterDeviceNotification(pRequest->hwnd, &dev, DEVICE_NOTIFY_WINDOW_HANDLE); + } + +LExit: + ReleaseRegKey(hk); + + return hr; +} + +static DWORD WINAPI WaiterThread( + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + DWORD dwRet = 0; + BOOL fAgain = FALSE; + BOOL fContinue = TRUE; + BOOL fNotify = FALSE; + BOOL fRet = FALSE; + MSG msg = { }; + MON_ADD_MESSAGE *pAddMessage = NULL; + MON_REMOVE_MESSAGE *pRemoveMessage = NULL; + MON_WAITER_CONTEXT *pWaiterContext = reinterpret_cast(pvContext); + DWORD dwRequestIndex; + DWORD dwNewRequestIndex; + // 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) + DWORD dwWait = 0; + DWORD uCurrentTime = 0; + DWORD uLastTimeInMs = ::GetTickCount(); + DWORD uDeltaInMs = 0; + DWORD cRequestsPendingBeforeLoop = 0; + LPWSTR sczDirectory = NULL; + bool rgfProcessedIndex[MON_MAX_MONITORS_PER_THREAD + 1] = { }; + MON_INTERNAL_TEMPORARY_WAIT * pInternalWait = NULL; + + // Ensure the thread has a message queue + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + pWaiterContext->fWaiterThreadMessageQueueInitialized = TRUE; + + do + { + dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE); + + uCurrentTime = ::GetTickCount(); + uDeltaInMs = uCurrentTime - uLastTimeInMs; + uLastTimeInMs = uCurrentTime; + + if (WAIT_OBJECT_0 == dwRet) + { + do + { + fRet = ::PeekMessage(&msg, reinterpret_cast(-1), 0, 0, PM_REMOVE); + fAgain = fRet; + if (fRet) + { + switch (msg.message) + { + case MON_MESSAGE_ADD: + pAddMessage = reinterpret_cast(msg.wParam); + + // Don't just blindly put it at the end of the array - it must be before any failing requests + // for WaitForMultipleObjects() to succeed + dwNewRequestIndex = pWaiterContext->cRequests - pWaiterContext->cRequestsFailing; + if (FAILED(pAddMessage->request.hrStatus)) + { + ++pWaiterContext->cRequestsFailing; + } + + hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH); + ExitOnFailure(hr, "Failed to insert additional handle"); + ++pWaiterContext->cHandles; + + // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL + if (MON_DIRECTORY == pAddMessage->request.type) + { + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = INVALID_HANDLE_VALUE; + } + + hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH); + ExitOnFailure(hr, "Failed to insert additional request struct"); + ++pWaiterContext->cRequests; + + pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request; + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = pAddMessage->handle; + + ReleaseNullMem(pAddMessage); + break; + + case MON_MESSAGE_REMOVE: + pRemoveMessage = reinterpret_cast(msg.wParam); + + // Find the request to remove + hr = FindRequestIndex(pWaiterContext, pRemoveMessage, &dwRequestIndex); + if (E_NOTFOUND == hr) + { + // Coordinator sends removes blindly to all waiter threads, so maybe this one wasn't intended for us + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to find request index for remove message"); + + hr = RemoveRequest(pWaiterContext, dwRequestIndex); + ExitOnFailure(hr, "Failed to remove request after request from coordinator thread."); + } + + MonRemoveMessageDestroy(pRemoveMessage); + pRemoveMessage = NULL; + break; + + case MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, PM_NOREMOVE)) + { + // If there is another a pending retry failed wait message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && FAILED(pWaiterContext->rgRequests[i].hrStatus)) + { + // This is not a failure, just record this in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + ExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, PM_NOREMOVE)) + { + // If there is another a pending retry successful wait message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && SUCCEEDED(pWaiterContext->rgRequests[i].hrStatus)) + { + // This is not a failure, just record this in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + ExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_NETWORK_STATUS_UPDATE: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_STATUS_UPDATE, MON_MESSAGE_NETWORK_STATUS_UPDATE, PM_NOREMOVE)) + { + // If there is another a pending network status update message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork) + { + // Failures here get recorded in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + ExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_DRIVE_STATUS_UPDATE: + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].sczOriginalPathRequest[0] == static_cast(msg.wParam)) + { + // Failures here get recorded in the request's status + if (static_cast(msg.lParam)) + { + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + } + else + { + // If the message says the drive is disconnected, don't even try to wait, just mark it as gone + hrTemp = E_PATHNOTFOUND; + } + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + ExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_DRIVE_QUERY_REMOVE: + pInternalWait = reinterpret_cast(msg.wParam); + // Only do any work if message is not yet out of date + // While it could become out of date while doing this processing, sending thread will check response to guard against this + if (pInternalWait->dwSendIteration == static_cast(msg.lParam)) + { + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgHandles[i + 1] == reinterpret_cast(pInternalWait->pvContext)) + { + // Release handles ASAP so the remove request will succeed + if (pWaiterContext->rgRequests[i].hNotify) + { + UnregisterDeviceNotification(pWaiterContext->rgRequests[i].hNotify); + pWaiterContext->rgRequests[i].hNotify = NULL; + } + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); + pWaiterContext->rgHandles[i + 1] = INVALID_HANDLE_VALUE; + + // Reply to unblock our reply to the remove request + pInternalWait->dwReceiveIteration = static_cast(msg.lParam); + if (!::SetEvent(pInternalWait->hWait)) + { + 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"); + } + + // Drive is disconnecting, don't even try to wait, just mark it as gone + hrTemp = E_PATHNOTFOUND; + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + ExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + break; + } + } + } + break; + + case MON_MESSAGE_STOP: + // Stop requested, so abort the whole thread + Trace(REPORT_DEBUG, "Waiter thread was told to stop"); + fAgain = FALSE; + fContinue = FALSE; + ExitFunction1(hr = static_cast(msg.wParam)); + + default: + Assert(false); + break; + } + } + } while (fAgain); + } + else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles) + { + // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist + dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1; + fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1); + + // Initiate re-waits before we notify callback, to ensure we don't miss a single update + hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1); + hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex); + ExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + // 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 + if (SUCCEEDED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus) && (fNotify || (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1))) + { + Trace(REPORT_DEBUG, "Changes detected, waiting for silence period index %u", dwRequestIndex); + + if (0 < pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) + { + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs = 0; + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = TRUE; + + if (!pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) + { + pWaiterContext->rgRequests[dwRequestIndex].fPendingFire = TRUE; + ++pWaiterContext->cRequestsPending; + } + } + else + { + // If no silence period, notify immediately + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); + } + } + } + else if (WAIT_TIMEOUT != dwRet) + { + ExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); + } + + // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire + // 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) + if (0 < pWaiterContext->cRequestsPending) + { + // Start at max value and find the lowest wait we can below that + dwWait = DWORD_MAX; + cRequestsPendingBeforeLoop = pWaiterContext->cRequestsPending; + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (pWaiterContext->rgRequests[i].fPendingFire) + { + if (0 == cRequestsPendingBeforeLoop) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); + ExitOnFailure(hr, "Phantom pending fires were found!"); + } + --cRequestsPendingBeforeLoop; + + dwRequestIndex = i; + + if (pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd) + { + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = FALSE; + } + else + { + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs += uDeltaInMs; + } + + // silence period has elapsed without further notifications, so reset pending-related variables, and finally fire a notify! + if (pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs >= pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) + { + Trace(REPORT_DEBUG, "Silence period surpassed, notifying %u ms late", pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs); + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); + } + else + { + // set dwWait to the shortest interval period so that if no changes occur, WaitForMultipleObjects + // wakes the thread back up when it's time to fire the next pending notification + if (dwWait > pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs) + { + dwWait = pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs; + } + } + } + } + + // Some post-loop list validation for sanity checking + if (0 < cRequestsPendingBeforeLoop) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA); + ExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait); + } + if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT); + ExitOnFailure(hr, "Pending fires exist, but wait was infinite", cRequestsPendingBeforeLoop); + } + } + } while (fContinue); + + // Don't bother firing pending notifications. We were told to stop monitoring, so client doesn't care. + +LExit: + ReleaseStr(sczDirectory); + MonAddMessageDestroy(pAddMessage); + MonRemoveMessageDestroy(pRemoveMessage); + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + MonRequestDestroy(pWaiterContext->rgRequests + i); + + switch (pWaiterContext->rgRequests[i].type) + { + case MON_DIRECTORY: + if (INVALID_HANDLE_VALUE != pWaiterContext->rgHandles[i + 1]) + { + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); + } + break; + case MON_REGKEY: + ReleaseHandle(pWaiterContext->rgHandles[i + 1]); + break; + default: + Assert(false); + } + } + + if (FAILED(hr)) + { + // If waiter thread fails, notify general callback of an error + Assert(pWaiterContext->vpfMonGeneral); + pWaiterContext->vpfMonGeneral(hr, pWaiterContext->pvContext); + + // And tell coordinator to shut all other waiters down + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to coordinator thread to stop (due to general failure)."); + } + } + + return hr; +} + +static void Notify( + __in HRESULT hr, + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REQUEST *pRequest + ) +{ + if (pRequest->fPendingFire) + { + --pWaiterContext->cRequestsPending; + } + + pRequest->fPendingFire = FALSE; + pRequest->fSkipDeltaAdd = FALSE; + pRequest->dwSilencePeriodInMs = 0; + + switch (pRequest->type) + { + case MON_DIRECTORY: + Assert(pWaiterContext->vpfMonDirectory); + pWaiterContext->vpfMonDirectory(hr, pRequest->sczOriginalPathRequest, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); + break; + case MON_REGKEY: + Assert(pWaiterContext->vpfMonRegKey); + pWaiterContext->vpfMonRegKey(hr, pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1], pRequest->regkey.kbKeyBitness, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); + break; + default: + Assert(false); + } +} + +static BOOL GetRecursiveFlag( + __in MON_REQUEST *pRequest, + __in DWORD dwIndex + ) +{ + if (pRequest->cPathHierarchy - 1 == dwIndex) + { + return pRequest->fRecursive; + } + else + { + return FALSE; + } +} + +static HRESULT FindRequestIndex( + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REMOVE_MESSAGE *pMessage, + __out DWORD *pdwIndex + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (pWaiterContext->rgRequests[i].type == pMessage->type) + { + switch (pWaiterContext->rgRequests[i].type) + { + case MON_DIRECTORY: + 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) + { + *pdwIndex = i; + ExitFunction1(hr = S_OK); + } + break; + case MON_REGKEY: + if (reinterpret_cast(pMessage->regkey.hkRoot) == reinterpret_cast(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) + { + *pdwIndex = i; + ExitFunction1(hr = S_OK); + } + break; + default: + Assert(false); + } + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +static HRESULT RemoveRequest( + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex + ) +{ + HRESULT hr = S_OK; + + MonRequestDestroy(pWaiterContext->rgRequests + dwRequestIndex); + + switch (pWaiterContext->rgRequests[dwRequestIndex].type) + { + case MON_DIRECTORY: + if (pWaiterContext->rgHandles[dwRequestIndex + 1] != INVALID_HANDLE_VALUE) + { + ::FindCloseChangeNotification(pWaiterContext->rgHandles[dwRequestIndex + 1]); + } + break; + case MON_REGKEY: + ReleaseHandle(pWaiterContext->rgHandles[dwRequestIndex + 1]); + break; + default: + Assert(false); + } + + if (pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) + { + --pWaiterContext->cRequestsPending; + } + + if (FAILED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus)) + { + --pWaiterContext->cRequestsFailing; + } + + MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), TRUE); + --pWaiterContext->cHandles; + MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), TRUE); + --pWaiterContext->cRequests; + + // Notify coordinator thread that a wait was removed + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast(::GetCurrentThreadId()), 0)) + { + ExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed."); + } + +LExit: + return hr; +} + +static REGSAM GetRegKeyBitness( + __in MON_REQUEST *pRequest + ) +{ + if (REG_KEY_32BIT == pRequest->regkey.kbKeyBitness) + { + return KEY_WOW64_32KEY; + } + else if (REG_KEY_64BIT == pRequest->regkey.kbKeyBitness) + { + return KEY_WOW64_64KEY; + } + else + { + return 0; + } +} + +static HRESULT DuplicateRemoveMessage( + __in MON_REMOVE_MESSAGE *pMessage, + __out MON_REMOVE_MESSAGE **ppMessage + ) +{ + HRESULT hr = S_OK; + + *ppMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + ExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message"); + + (*ppMessage)->type = pMessage->type; + (*ppMessage)->fRecursive = pMessage->fRecursive; + + switch (pMessage->type) + { + case MON_DIRECTORY: + hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0); + ExitOnFailure(hr, "Failed to copy directory"); + break; + case MON_REGKEY: + (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot; + (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness; + hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0); + ExitOnFailure(hr, "Failed to copy subkey"); + break; + default: + Assert(false); + break; + } + +LExit: + return hr; +} + +static LRESULT CALLBACK MonWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HRESULT hr = S_OK; + DEV_BROADCAST_HDR *pHdr = NULL; + DEV_BROADCAST_HANDLE *pHandle = NULL; + DEV_BROADCAST_VOLUME *pVolume = NULL; + DWORD dwUnitMask = 0; + DWORD er = ERROR_SUCCESS; + WCHAR chDrive = L'\0'; + BOOL fArrival = FALSE; + BOOL fReturnTrue = FALSE; + CREATESTRUCT *pCreateStruct = NULL; + MON_WAITER_CONTEXT *pWaiterContext = NULL; + MON_STRUCT *pm = NULL; + + // keep track of the MON_STRUCT pointer that was passed in on init, associate it with the window + if (WM_CREATE == uMsg) + { + pCreateStruct = reinterpret_cast(lParam); + if (pCreateStruct) + { + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); + } + } + else if (WM_NCDESTROY == uMsg) + { + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + } + + // Note this message ONLY comes in through WndProc, it isn't visible from the GetMessage loop. + else if (WM_DEVICECHANGE == uMsg) + { + if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam) + { + fArrival = DBT_DEVICEARRIVAL == wParam; + + pHdr = reinterpret_cast(lParam); + if (DBT_DEVTYP_VOLUME == pHdr->dbch_devicetype) + { + pVolume = reinterpret_cast(lParam); + dwUnitMask = pVolume->dbcv_unitmask; + chDrive = L'a'; + while (0 < dwUnitMask) + { + if (dwUnitMask & 0x1) + { + // This drive had a status update, so send it out to all threads + if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast(chDrive), static_cast(fArrival))) + { + ExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE"); + } + } + dwUnitMask >>= 1; + ++chDrive; + + if (chDrive == 'z') + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask); + } + } + } + } + // We can only process device query remove messages if we have a MON_STRUCT pointer + else if (DBT_DEVICEQUERYREMOVE == wParam) + { + pm = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + if (!pm) + { + hr = E_POINTER; + ExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored"); + } + + fReturnTrue = TRUE; + + pHdr = reinterpret_cast(lParam); + if (DBT_DEVTYP_HANDLE == pHdr->dbch_devicetype) + { + // 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 + // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread + pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed"); + + pHandle = reinterpret_cast(lParam); + pm->internalWait.pvContext = pHandle->dbch_handle; + pm->internalWait.dwReceiveIteration = pm->internalWait.dwSendIteration - 1; + // This drive had a status update, so send it out to all threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast(&pm->internalWait), static_cast(pm->internalWait.dwSendIteration))) + { + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message"); + } + } + + er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); + // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response + if (WAIT_OBJECT_0 == er) + { + // If the response ID matches what we sent, we actually got a valid reply! + if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration) + { + TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply"); + } + } + else if (WAIT_TIMEOUT == er) + { + TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message"); + } + else + { + ExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); + } + ++pm->internalWait.dwSendIteration; + } + } + } + +LExit: + if (pm) + { + ReleaseHandle(pm->internalWait.hWait); + } + + if (fReturnTrue) + { + return TRUE; + } + else + { + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + } +} + +static HRESULT CreateMonWindow( + __in MON_STRUCT *pm, + __out HWND *pHwnd + ) +{ + HRESULT hr = S_OK; + WNDCLASSW wc = { }; + + wc.lpfnWndProc = MonWndProc; + wc.hInstance = ::GetModuleHandleW(NULL); + wc.lpszClassName = MONUTIL_WINDOW_CLASS; + if (!::RegisterClassW(&wc)) + { + if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError()) + { + ExitWithLastError(hr, "Failed to register MonUtil window class."); + } + } + + *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm); + ExitOnNullWithLastError(*pHwnd, hr, "Failed to create window."); + + // 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 + // SWP_NOACTIVATE is important so the currently active window doesn't lose focus + SetWindowPos(*pHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_DEFERERASE | SWP_NOACTIVATE); + +LExit: + return hr; +} + +static HRESULT WaitForNetworkChanges( + __inout HANDLE *phMonitor, + __in MON_STRUCT *pm + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + DWORD dwBytesReturned = 0; + WSACOMPLETION wsaCompletion = { }; + WSAQUERYSET qsRestrictions = { }; + + qsRestrictions.dwSize = sizeof(WSAQUERYSET); + qsRestrictions.dwNameSpace = NS_NLA; + + if (NULL != *phMonitor) + { + ::WSALookupServiceEnd(*phMonitor); + *phMonitor = NULL; + } + + if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor)) + { + hr = HRESULT_FROM_WIN32(::WSAGetLastError()); + ExitOnFailure(hr, "WSALookupServiceBegin() failed"); + } + + wsaCompletion.Type = NSP_NOTIFY_HWND; + wsaCompletion.Parameters.WindowMessage.hWnd = pm->hwnd; + wsaCompletion.Parameters.WindowMessage.uMsg = MON_MESSAGE_NETWORK_STATUS_UPDATE; + nResult = ::WSANSPIoctl(*phMonitor, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &dwBytesReturned, &wsaCompletion); + if (SOCKET_ERROR != nResult || WSA_IO_PENDING != ::WSAGetLastError()) + { + hr = HRESULT_FROM_WIN32(::WSAGetLastError()); + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError()); + } + +LExit: + return hr; +} + +static HRESULT UpdateWaitStatus( + __in HRESULT hrNewStatus, + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex, + __out_opt DWORD *pdwNewRequestIndex + ) +{ + HRESULT hr = S_OK; + DWORD dwNewRequestIndex; + MON_REQUEST *pRequest = pWaiterContext->rgRequests + dwRequestIndex; + + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwRequestIndex; + } + + if (SUCCEEDED(pRequest->hrStatus) || SUCCEEDED(hrNewStatus)) + { + // If it's a network wait, notify as long as it's new status is successful because we *may* have lost some changes + // before the wait was re-initiated. Otherwise, only notify if there was an interesting status change + if (SUCCEEDED(pRequest->hrStatus) != SUCCEEDED(hrNewStatus) || (pRequest->fNetwork && SUCCEEDED(hrNewStatus))) + { + Notify(hrNewStatus, pWaiterContext, pRequest); + } + + if (SUCCEEDED(pRequest->hrStatus) && FAILED(hrNewStatus)) + { + // If it's a network wait, notify coordinator thread that a network wait is failing + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0)) + { + ExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail"); + } + + // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle + ++pWaiterContext->cRequestsFailing; + dwNewRequestIndex = pWaiterContext->cRequests - 1; + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); + // Reset pRequest to the newly swapped item + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwNewRequestIndex; + } + } + else if (FAILED(pRequest->hrStatus) && SUCCEEDED(hrNewStatus)) + { + Assert(pWaiterContext->cRequestsFailing > 0); + // If it's a network wait, notify coordinator thread that a network wait is succeeding again + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0)) + { + ExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again"); + } + + --pWaiterContext->cRequestsFailing; + dwNewRequestIndex = 0; + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); + // Reset pRequest to the newly swapped item + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwNewRequestIndex; + } + } + } + + pRequest->hrStatus = hrNewStatus; + +LExit: + return hr; +} diff --git a/src/dutil/osutil.cpp b/src/dutil/osutil.cpp new file mode 100644 index 00000000..d1a4dd9a --- /dev/null +++ b/src/dutil/osutil.cpp @@ -0,0 +1,188 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; +DWORD vdwOsServicePack = 0; + +/******************************************************************** + OsGetVersion + +********************************************************************/ +extern "C" void DAPI OsGetVersion( + __out OS_VERSION* pVersion, + __out DWORD* pdwServicePack + ) +{ + OSVERSIONINFOEXW ovi = { }; + + if (OS_VERSION_UNKNOWN == vOsVersion) + { + ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + ::GetVersionExW(reinterpret_cast(&ovi)); // only fails if version info size is set incorrectly. + + vdwOsServicePack = static_cast(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor; + if (4 == ovi.dwMajorVersion) + { + vOsVersion = OS_VERSION_WINNT; + } + else if (5 == ovi.dwMajorVersion) + { + if (0 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WIN2000; + } + else if (1 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WINXP; + } + else if (2 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WIN2003; + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + else if (6 == ovi.dwMajorVersion) + { + if (0 == ovi.dwMinorVersion) + { + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008; + } + else if (1 == ovi.dwMinorVersion) + { + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2; + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + + *pVersion = vOsVersion; + *pdwServicePack = vdwOsServicePack; +} + +extern "C" HRESULT DAPI OsCouldRunPrivileged( + __out BOOL* pfPrivileged + ) +{ + HRESULT hr = S_OK; + BOOL fUacEnabled = FALSE; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup = NULL; + + // Do a best effort check to see if UAC is enabled on this machine. + OsIsUacEnabled(&fUacEnabled); + + // If UAC is enabled then the process could run privileged by asking to elevate. + if (fUacEnabled) + { + *pfPrivileged = TRUE; + } + else // no UAC so only privilged if user is in administrators group. + { + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (*pfPrivileged) + { + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) + { + *pfPrivileged = FALSE; + } + } + } + + ReleaseSid(AdministratorsGroup); + return hr; +} + +extern "C" HRESULT DAPI OsIsRunningPrivileged( + __out BOOL* pfPrivileged + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + HANDLE hToken = NULL; + TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault; + DWORD dwSize = 0; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup = NULL; + + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + ExitOnLastError(hr, "Failed to open process token."); + } + + if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) + { + *pfPrivileged = (TokenElevationTypeFull == elevationType); + ExitFunction1(hr = S_OK); + } + + // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check + er = ::GetLastError(); + if (ERROR_INVALID_FUNCTION == er) + { + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to get process token information."); + + // Fallback to this check for some OS's (like XP) + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (*pfPrivileged) + { + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) + { + *pfPrivileged = FALSE; + } + } + +LExit: + ReleaseSid(AdministratorsGroup); + + if (hToken) + { + ::CloseHandle(hToken); + } + + return hr; +} + +extern "C" HRESULT DAPI OsIsUacEnabled( + __out BOOL* pfUacEnabled + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + DWORD dwUacEnabled = 0; + + *pfUacEnabled = FALSE; // assume UAC not enabled. + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open system policy key to detect UAC."); + + hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to read registry value to detect UAC."); + + *pfUacEnabled = (0 != dwUacEnabled); + +LExit: + ReleaseRegKey(hk); + + return hr; +} diff --git a/src/dutil/path2utl.cpp b/src/dutil/path2utl.cpp new file mode 100644 index 00000000..8f5f03a1 --- /dev/null +++ b/src/dutil/path2utl.cpp @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +DAPI_(HRESULT) PathCanonicalizePath( + __in_z LPCWSTR wzPath, + __deref_out_z LPWSTR* psczCanonicalized + ) +{ + HRESULT hr = S_OK; + int cch = MAX_PATH + 1; + + hr = StrAlloc(psczCanonicalized, cch); + ExitOnFailure(hr, "Failed to allocate string for the canonicalized path."); + + if (::PathCanonicalizeW(*psczCanonicalized, wzPath)) + { + hr = S_OK; + } + else + { + ExitFunctionWithLastError(hr); + } + +LExit: + return hr; +} + +DAPI_(HRESULT) PathDirectoryContainsPath( + __in_z LPCWSTR wzDirectory, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + LPWSTR sczDirectory = NULL; + LPWSTR sczOriginalPath = NULL; + LPWSTR sczOriginalDirectory = NULL; + + hr = PathCanonicalizePath(wzPath, &sczOriginalPath); + ExitOnFailure(hr, "Failed to canonicalize the path."); + + hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory); + ExitOnFailure(hr, "Failed to canonicalize the directory."); + + if (!sczOriginalPath || !*sczOriginalPath) + { + ExitFunction1(hr = S_FALSE); + } + if (!sczOriginalDirectory || !*sczOriginalDirectory) + { + ExitFunction1(hr = S_FALSE); + } + + sczPath = sczOriginalPath; + sczDirectory = sczOriginalDirectory; + + for (; *sczDirectory;) + { + if (!*sczPath) + { + ExitFunction1(hr = S_FALSE); + } + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1)) + { + ExitFunction1(hr = S_FALSE); + } + + ++sczDirectory; + ++sczPath; + } + + --sczDirectory; + if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath) + { + hr = S_OK; + } + else + { + hr = S_FALSE; + } + +LExit: + ReleaseStr(sczOriginalPath); + ReleaseStr(sczOriginalDirectory); + return hr; +} diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp new file mode 100644 index 00000000..c508dd32 --- /dev/null +++ b/src/dutil/pathutil.cpp @@ -0,0 +1,1009 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define PATH_GOOD_ENOUGH 64 + + +DAPI_(HRESULT) PathCommandLineAppend( + __deref_out_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ) +{ + HRESULT hr = S_OK; + LPWSTR sczQuotedArg = NULL; + BOOL fRequiresQuoting = FALSE; + DWORD dwMaxEscapedSize = 0; + + // Loop through the argument determining if it needs to be quoted and what the maximum + // size would be if there are escape characters required. + for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) + { + // Arguments with whitespace need quoting. + if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) + { + fRequiresQuoting = TRUE; + } + else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. + { + fRequiresQuoting = TRUE; + ++dwMaxEscapedSize; + } + else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. + { + ++dwMaxEscapedSize; + } + + ++dwMaxEscapedSize; + } + + // If we found anything in the argument that requires our argument to be quoted + if (fRequiresQuoting) + { + hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. + ExitOnFailure(hr, "Failed to allocate argument to be quoted."); + + LPCWSTR pwz = wzArgument; + LPWSTR pwzQuoted = sczQuotedArg; + + *pwzQuoted = L'"'; + ++pwzQuoted; + while (*pwz) + { + DWORD dwBackslashes = 0; + while (L'\\' == *pwz) + { + ++dwBackslashes; + ++pwz; + } + + // Escape all backslashes at the end of the string. + if (!*pwz) + { + dwBackslashes *= 2; + } + else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. + { + dwBackslashes = dwBackslashes * 2 + 1; + } + // the backslashes don't have to be escaped. + + // Add the appropriate number of backslashes + for (DWORD i = 0; i < dwBackslashes; ++i) + { + *pwzQuoted = L'\\'; + ++pwzQuoted; + } + + // If there is a character, add it after all the escaped backslashes + if (*pwz) + { + *pwzQuoted = *pwz; + ++pwz; + ++pwzQuoted; + } + } + + *pwzQuoted = L'"'; + ++pwzQuoted; + *pwzQuoted = L'\0'; // ensure the arg is null terminated. + } + + // If there is already data in the command line, append a space before appending the + // argument. + if (*psczCommandLine && **psczCommandLine) + { + hr = StrAllocConcat(psczCommandLine, L" ", 0); + ExitOnFailure(hr, "Failed to append space to command line with existing data."); + } + + hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); + ExitOnFailure(hr, "Failed to copy command line argument."); + +LExit: + ReleaseStr(sczQuotedArg); + + return hr; +} + + +DAPI_(LPWSTR) PathFile( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + { + return NULL; + } + + LPWSTR wzFile = const_cast(wzPath); + for (LPWSTR wz = wzFile; *wz; ++wz) + { + // valid delineators + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) + { + wzFile = wz + 1; + } + } + + return wzFile; +} + + +DAPI_(LPCWSTR) PathExtension( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + { + return NULL; + } + + // Find the last dot in the last thing that could be a file. + LPCWSTR wzExtension = NULL; + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + if (L'\\' == *wz || L'/' == *wz || L':' == *wz) + { + wzExtension = NULL; + } + else if (L'.' == *wz) + { + wzExtension = wz; + } + } + + return wzExtension; +} + + +DAPI_(HRESULT) PathGetDirectory( + __in_z LPCWSTR wzPath, + __out LPWSTR *psczDirectory + ) +{ + HRESULT hr = S_OK; + DWORD cchDirectory = DWORD_MAX; + + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + // valid delineators: + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) + { + cchDirectory = static_cast(wz - wzPath) + 1; + } + } + + if (DWORD_MAX == cchDirectory) + { + // we were given just a file name, so there's no directory available + return S_FALSE; + } + + if (wzPath[0] == L'\"') + { + ++wzPath; + --cchDirectory; + } + + hr = StrAllocString(psczDirectory, wzPath, cchDirectory); + ExitOnFailure(hr, "Failed to copy directory."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathExpand( + __out LPWSTR *psczFullPath, + __in_z LPCWSTR wzRelativePath, + __in DWORD dwResolveFlags + ) +{ + Assert(wzRelativePath && *wzRelativePath); + + HRESULT hr = S_OK; + DWORD cch = 0; + LPWSTR sczExpandedPath = NULL; + DWORD cchExpandedPath = 0; + + LPWSTR sczFullPath = NULL; + + // + // First, expand any environment variables. + // + if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT) + { + cchExpandedPath = PATH_GOOD_ENOUGH; + + hr = StrAlloc(&sczExpandedPath, cchExpandedPath); + ExitOnFailure(hr, "Failed to allocate space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + cchExpandedPath = cch; + hr = StrAlloc(&sczExpandedPath, cchExpandedPath); + ExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + } + } + + if (MAX_PATH < cch) + { + hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet + if (E_INVALIDARG == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); + + hr = StrMaxLength(sczExpandedPath, reinterpret_cast(&cchExpandedPath)); + ExitOnFailure(hr, "Failed to get max length of expanded path."); + } + } + + // + // Second, get the full path. + // + if (dwResolveFlags & PATH_EXPAND_FULLPATH) + { + LPWSTR wzFileName = NULL; + LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; + DWORD cchFullPath = PATH_GOOD_ENOUGH < cchExpandedPath ? cchExpandedPath : PATH_GOOD_ENOUGH; + + hr = StrAlloc(&sczFullPath, cchFullPath); + ExitOnFailure(hr, "Failed to allocate space for full path."); + + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + } + else if (cchFullPath < cch) + { + cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed + hr = StrAlloc(&sczFullPath, cchFullPath); + ExitOnFailure(hr, "Failed to re-allocate more space for full path."); + + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); + if (0 == cch) + { + ExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + } + else if (cchFullPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + } + } + + if (MAX_PATH < cch) + { + hr = PathPrefix(&sczFullPath); + ExitOnFailure(hr, "Failed to prefix long path after expanding."); + } + } + else + { + sczFullPath = sczExpandedPath; + sczExpandedPath = NULL; + } + + hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); + ExitOnFailure(hr, "Failed to copy relative path into full path."); + +LExit: + ReleaseStr(sczFullPath); + ReleaseStr(sczExpandedPath); + + return hr; +} + + +DAPI_(HRESULT) PathPrefix( + __inout LPWSTR *psczFullPath + ) +{ + Assert(psczFullPath && *psczFullPath); + + HRESULT hr = S_OK; + LPWSTR wzFullPath = *psczFullPath; + DWORD_PTR cbFullPath = 0; + + if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || + (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && + L':' == wzFullPath[1] && + L'\\' == wzFullPath[2]) // normal path + { + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); + ExitOnFailure(hr, "Failed to add prefix to file path."); + } + else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC + { + // ensure that we're not already prefixed + if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) + { + hr = StrSize(*psczFullPath, &cbFullPath); + ExitOnFailure(hr, "Failed to get size of full path."); + + memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); + + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); + ExitOnFailure(hr, "Failed to add prefix to UNC path."); + } + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathFixedBackslashTerminate( + __inout_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD_PTR cchPath + ) +{ + HRESULT hr = S_OK; + size_t cchLength = 0; + + hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); + ExitOnFailure(hr, "Failed to get length of path."); + + if (cchLength >= cchPath) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else if (L'\\' != wzPath[cchLength - 1]) + { + wzPath[cchLength] = L'\\'; + wzPath[cchLength + 1] = L'\0'; + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathBackslashTerminate( + __inout LPWSTR* psczPath + ) +{ + Assert(psczPath && *psczPath); + + HRESULT hr = S_OK; + DWORD_PTR cchPath = 0; + size_t cchLength = 0; + + hr = StrMaxLength(*psczPath, &cchPath); + ExitOnFailure(hr, "Failed to get size of path string."); + + hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); + ExitOnFailure(hr, "Failed to get length of path."); + + if (L'\\' != (*psczPath)[cchLength - 1]) + { + hr = StrAllocConcat(psczPath, L"\\", 1); + ExitOnFailure(hr, "Failed to concat backslash onto string."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathForCurrentProcess( + __inout LPWSTR *psczFullPath, + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + DWORD cch = MAX_PATH; + + do + { + hr = StrAlloc(psczFullPath, cch); + ExitOnFailure(hr, "Failed to allocate string for module path."); + + DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); + if (0 == cchRequired) + { + ExitWithLastError(hr, "Failed to get path for executing process."); + } + else if (cchRequired == cch) + { + cch = cchRequired + 1; + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else + { + hr = S_OK; + } + } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathRelativeToModule( + __inout LPWSTR *psczFullPath, + __in_opt LPCWSTR wzFileName, + __in_opt HMODULE hModule + ) +{ + HRESULT hr = PathForCurrentProcess(psczFullPath, hModule); + ExitOnFailure(hr, "Failed to get current module path."); + + hr = PathGetDirectory(*psczFullPath, psczFullPath); + ExitOnFailure(hr, "Failed to get current module directory."); + + if (wzFileName) + { + hr = PathConcat(*psczFullPath, wzFileName, psczFullPath); + ExitOnFailure(hr, "Failed to append filename."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathCreateTempFile( + __in_opt LPCWSTR wzDirectory, + __in_opt __format_string LPCWSTR wzFileNameTemplate, + __in DWORD dwUniqueCount, + __in DWORD dwFileAttributes, + __out_opt LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ) +{ + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); + + HRESULT hr = S_OK; + + LPWSTR sczTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + LPWSTR scz = NULL; + LPWSTR sczTempFile = NULL; + + if (wzDirectory && *wzDirectory) + { + hr = StrAllocString(&sczTempPath, wzDirectory, 0); + ExitOnFailure(hr, "Failed to copy temp path."); + } + else + { + hr = StrAlloc(&sczTempPath, cchTempPath); + ExitOnFailure(hr, "Failed to allocate memory for the temp path."); + + if (!::GetTempPathW(cchTempPath, sczTempPath)) + { + ExitWithLastError(hr, "Failed to get temp path."); + } + } + + if (wzFileNameTemplate && *wzFileNameTemplate) + { + for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); + ExitOnFailure(hr, "Failed to allocate memory for file template."); + + hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); + ExitOnFailure(hr, "Failed to allocate temp file name."); + + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); + } + } + } + + // If we were not able to or we did not try to create a temp file, ask + // the system to provide us a temp file using its built-in mechanism. + if (INVALID_HANDLE_VALUE == hTempFile) + { + hr = StrAlloc(&sczTempFile, MAX_PATH); + ExitOnFailure(hr, "Failed to allocate memory for the temp path"); + + if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) + { + ExitWithLastError(hr, "Failed to create new temp file name."); + } + + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + ExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); + } + } + + // If the caller wanted the temp file name or handle, return them here. + if (psczTempFile) + { + hr = StrAllocString(psczTempFile, sczTempFile, 0); + ExitOnFailure(hr, "Failed to copy temp file string."); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + if (INVALID_HANDLE_VALUE != hTempFile) + { + ::CloseHandle(hTempFile); + } + + ReleaseStr(scz); + ReleaseStr(sczTempFile); + ReleaseStr(sczTempPath); + + return hr; +} + + +DAPI_(HRESULT) PathCreateTimeBasedTempFile( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzPrefix, + __in_z_opt LPCWSTR wzPostfix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ) +{ + HRESULT hr = S_OK; + BOOL fRetry = FALSE; + WCHAR wzTempPath[MAX_PATH] = { }; + LPWSTR sczPrefix = NULL; + LPWSTR sczPrefixFolder = NULL; + SYSTEMTIME time = { }; + + LPWSTR sczTempPath = NULL; + HANDLE hTempFile = INVALID_HANDLE_VALUE; + DWORD dwAttempts = 0; + + if (wzDirectory && *wzDirectory) + { + hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix); + ExitOnFailure(hr, "Failed to combine directory and log prefix."); + } + else + { + if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + ExitWithLastError(hr, "Failed to get temp folder."); + } + + hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); + ExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); + } + + hr = PathGetDirectory(sczPrefix, &sczPrefixFolder); + if (S_OK == hr) + { + hr = DirEnsureExists(sczPrefixFolder, NULL); + ExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); + } + + if (!wzPostfix) + { + wzPostfix = L""; + } + + do + { + fRetry = FALSE; + ++dwAttempts; + + ::GetLocalTime(&time); + + // Log format: pre YYYY MM dd hh mm ss post ext + 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); + ExitOnFailure(hr, "failed to allocate memory for the temp path"); + + hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // If the file already exists, just try again. + DWORD er = ::GetLastError(); + if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er) + { + ::Sleep(100); + + if (10 > dwAttempts) + { + er = ERROR_SUCCESS; + fRetry = TRUE; + } + } + + hr = HRESULT_FROM_WIN32(er); + ExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); + } + } while (fRetry); + + if (psczTempFile) + { + hr = StrAllocString(psczTempFile, sczTempPath, 0); + ExitOnFailure(hr, "Failed to copy temp path to return."); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(sczTempPath); + ReleaseStr(sczPrefixFolder); + ReleaseStr(sczPrefix); + + return hr; +} + + +DAPI_(HRESULT) PathCreateTempDirectory( + __in_opt LPCWSTR wzDirectory, + __in __format_string LPCWSTR wzDirectoryNameTemplate, + __in DWORD dwUniqueCount, + __out LPWSTR* psczTempDirectory + ) +{ + AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified."); + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); + + HRESULT hr = S_OK; + + LPWSTR sczTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + LPWSTR scz = NULL; + + if (wzDirectory && *wzDirectory) + { + hr = StrAllocString(&sczTempPath, wzDirectory, 0); + ExitOnFailure(hr, "Failed to copy temp path."); + + hr = PathBackslashTerminate(&sczTempPath); + ExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); + } + else + { + hr = StrAlloc(&sczTempPath, cchTempPath); + ExitOnFailure(hr, "Failed to allocate memory for the temp path."); + + if (!::GetTempPathW(cchTempPath, sczTempPath)) + { + ExitWithLastError(hr, "Failed to get temp path."); + } + } + + for (DWORD i = 1; i <= dwUniqueCount; ++i) + { + hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i); + ExitOnFailure(hr, "Failed to allocate memory for directory name template."); + + hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz); + ExitOnFailure(hr, "Failed to allocate temp directory name."); + + if (!::CreateDirectoryW(*psczTempDirectory, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + continue; + } + else if (ERROR_PATH_NOT_FOUND == er) + { + hr = DirEnsureExists(*psczTempDirectory, NULL); + break; + } + else + { + hr = HRESULT_FROM_WIN32(er); + break; + } + } + else + { + hr = S_OK; + break; + } + } + ExitOnFailure(hr, "Failed to create temp directory."); + + hr = PathBackslashTerminate(psczTempDirectory); + ExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); + +LExit: + ReleaseStr(scz); + ReleaseStr(sczTempPath); + + return hr; +} + + +DAPI_(HRESULT) PathGetKnownFolder( + __in int csidl, + __out LPWSTR* psczKnownFolder + ) +{ + HRESULT hr = S_OK; + + hr = StrAlloc(psczKnownFolder, MAX_PATH); + ExitOnFailure(hr, "Failed to allocate memory for known folder."); + + hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); + ExitOnFailure(hr, "Failed to get known folder path."); + + hr = PathBackslashTerminate(psczKnownFolder); + ExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); + +LExit: + return hr; +} + + +DAPI_(BOOL) PathIsAbsolute( + __in_z LPCWSTR wzPath + ) +{ + DWORD dwLength = lstrlenW(wzPath); + return (1 < dwLength) && (wzPath[0] == L'\\') || (wzPath[1] == L':'); +} + + +DAPI_(HRESULT) PathConcat( + __in_opt LPCWSTR wzPath1, + __in_opt LPCWSTR wzPath2, + __deref_out_z LPWSTR* psczCombined + ) +{ + HRESULT hr = S_OK; + + if (!wzPath2 || !*wzPath2) + { + hr = StrAllocString(psczCombined, wzPath1, 0); + ExitOnFailure(hr, "Failed to copy just path1 to output."); + } + else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) + { + hr = StrAllocString(psczCombined, wzPath2, 0); + ExitOnFailure(hr, "Failed to copy just path2 to output."); + } + else + { + hr = StrAllocString(psczCombined, wzPath1, 0); + ExitOnFailure(hr, "Failed to copy path1 to output."); + + hr = PathBackslashTerminate(psczCombined); + ExitOnFailure(hr, "Failed to backslashify."); + + hr = StrAllocConcat(psczCombined, wzPath2, 0); + ExitOnFailure(hr, "Failed to append path2 to output."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathEnsureQuoted( + __inout LPWSTR* ppszPath, + __in BOOL fDirectory + ) +{ + Assert(ppszPath && *ppszPath); + + HRESULT hr = S_OK; + size_t cchPath = 0; + + hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath); + ExitOnFailure(hr, "Failed to get the length of the path."); + + // Handle simple special cases. + if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0])) + { + hr = StrAllocString(ppszPath, L"\"\"", 2); + ExitOnFailure(hr, "Failed to allocate a quoted empty string."); + + ExitFunction(); + } + + if (L'"' != (*ppszPath)[0]) + { + hr = StrAllocPrefix(ppszPath, L"\"", 1); + ExitOnFailure(hr, "Failed to allocate an opening quote."); + + // Add a char for the opening quote. + ++cchPath; + } + + if (L'"' != (*ppszPath)[cchPath - 1]) + { + hr = StrAllocConcat(ppszPath, L"\"", 1); + ExitOnFailure(hr, "Failed to allocate a closing quote."); + + // Add a char for the closing quote. + ++cchPath; + } + + if (fDirectory) + { + if (L'\\' != (*ppszPath)[cchPath - 2]) + { + // Change the last char to a backslash and re-append the closing quote. + (*ppszPath)[cchPath - 1] = L'\\'; + + hr = StrAllocConcat(ppszPath, L"\"", 1); + ExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); + } + } + +LExit: + + return hr; +} + + +DAPI_(HRESULT) PathCompare( + __in_z LPCWSTR wzPath1, + __in_z LPCWSTR wzPath2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath1 = NULL; + LPWSTR sczPath2 = NULL; + + hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + ExitOnFailure(hr, "Failed to expand path1."); + + hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + ExitOnFailure(hr, "Failed to expand path2."); + + *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); + +LExit: + ReleaseStr(sczPath2); + ReleaseStr(sczPath1); + + return hr; +} + + +DAPI_(HRESULT) PathCompress( + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + HANDLE hPath = INVALID_HANDLE_VALUE; + + hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (INVALID_HANDLE_VALUE == hPath) + { + ExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); + } + + DWORD dwBytesReturned = 0; + USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT; + if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL)) + { + // ignore compression attempts on file systems that don't support it + DWORD er = ::GetLastError(); + if (ERROR_INVALID_FUNCTION != er) + { + ExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); + } + } + +LExit: + ReleaseFile(hPath); + + return hr; +} + +DAPI_(HRESULT) PathGetHierarchyArray( + __in_z LPCWSTR wzPath, + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczPathArray, + __inout LPUINT pcPathArray + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPathCopy = NULL; + LPWSTR sczNewPathCopy = NULL; + DWORD cArraySpacesNeeded = 0; + + for (int i = 0; i < lstrlenW(wzPath); ++i) + { + if (wzPath[i] == L'\\') + { + ++cArraySpacesNeeded; + } + } + if (wzPath[lstrlenW(wzPath) - 1] != L'\\') + { + ++cArraySpacesNeeded; + } + + // 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. + if (wzPath[0] == L'\\' && wzPath[1] == L'\\') + { + cArraySpacesNeeded -= 3; + } + + Assert(cArraySpacesNeeded >= 1); + + hr = MemEnsureArraySize(reinterpret_cast(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); + ExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); + *pcPathArray = cArraySpacesNeeded; + + hr = StrAllocString(&sczPathCopy, wzPath, 0); + ExitOnFailure(hr, "Failed to allocate copy of original path"); + + for (DWORD i = 0; i < cArraySpacesNeeded; ++i) + { + hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); + ExitOnFailure(hr, "Failed to copy path"); + + // 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 + if (wzPath[lstrlenW(sczPathCopy) - 1] == L'\\') + { + sczPathCopy[lstrlenW(sczPathCopy) - 1] = L'\0'; + } + + hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); + ExitOnFailure(hr, "Failed to get directory portion of path"); + + ReleaseStr(sczPathCopy); + sczPathCopy = sczNewPathCopy; + sczNewPathCopy = NULL; + } + + hr = S_OK; + +LExit: + ReleaseStr(sczPathCopy); + + return hr; +} diff --git a/src/dutil/perfutil.cpp b/src/dutil/perfutil.cpp new file mode 100644 index 00000000..5c4e0774 --- /dev/null +++ b/src/dutil/perfutil.cpp @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter +static double vdFrequency = 1; + + +/******************************************************************** + PerfInitialize - initializes internal static variables + +********************************************************************/ +extern "C" void DAPI PerfInitialize( + ) +{ + LARGE_INTEGER liFrequency = { }; + + // + // check for high perf counter + // + if (!::QueryPerformanceFrequency(&liFrequency)) + { + vfHighPerformanceCounter = FALSE; + vdFrequency = 1000; // ticks are measured in milliseconds + } + else + vdFrequency = static_cast(liFrequency.QuadPart); +} + + +/******************************************************************** + PerfClickTime - resets the clicker, or returns elapsed time since last call + + NOTE: if pliElapsed is NULL, resets the elapsed time + if pliElapsed is not NULL, returns perf number since last call to PerfClickTime() +********************************************************************/ +extern "C" void DAPI PerfClickTime( + __out_opt LARGE_INTEGER* pliElapsed + ) +{ + static LARGE_INTEGER liStart = { }; + LARGE_INTEGER* pli = pliElapsed; + + if (!pli) // if elapsed time time was not requested, reset the start time + pli = &liStart; + + if (vfHighPerformanceCounter) + ::QueryPerformanceCounter(pli); + else + pli->QuadPart = ::GetTickCount(); + + if (pliElapsed) + pliElapsed->QuadPart -= liStart.QuadPart; +} + + +/******************************************************************** + PerfConvertToSeconds - converts perf number to seconds + +********************************************************************/ +extern "C" double DAPI PerfConvertToSeconds( + __in const LARGE_INTEGER* pli + ) +{ + Assert(0 < vdFrequency); + return pli->QuadPart / vdFrequency; +} diff --git a/src/dutil/polcutil.cpp b/src/dutil/polcutil.cpp new file mode 100644 index 00000000..1cc29e61 --- /dev/null +++ b/src/dutil/polcutil.cpp @@ -0,0 +1,111 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\"; + +static HRESULT OpenPolicyKey( + __in_z LPCWSTR wzPolicyPath, + __out HKEY* phk + ); + + +extern "C" HRESULT DAPI PolcReadNumber( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in DWORD dwDefault, + __out DWORD* pdw + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + + hr = OpenPolicyKey(wzPolicyPath, &hk); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + ExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + + hr = RegReadNumber(hk, wzPolicyName, pdw); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + ExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + +LExit: + ReleaseRegKey(hk); + + if (S_FALSE == hr || FAILED(hr)) + { + *pdw = dwDefault; + } + + return hr; +} + +extern "C" HRESULT DAPI PolcReadString( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in_z_opt LPCWSTR wzDefault, + __deref_out_z LPWSTR* pscz + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + + hr = OpenPolicyKey(wzPolicyPath, &hk); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + ExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + + hr = RegReadString(hk, wzPolicyName, pscz); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + ExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + +LExit: + ReleaseRegKey(hk); + + if (S_FALSE == hr || FAILED(hr)) + { + if (NULL == wzDefault) + { + ReleaseNullStr(*pscz); + } + else + { + hr = StrAllocString(pscz, wzDefault, 0); + } + } + + return hr; +} + + +// internal functions + +static HRESULT OpenPolicyKey( + __in_z LPCWSTR wzPolicyPath, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath); + ExitOnFailure(hr, "Failed to combine logging path with root path."); + + hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk); + ExitOnFailure(hr, "Failed to open policy registry key."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h new file mode 100644 index 00000000..3acbca43 --- /dev/null +++ b/src/dutil/precomp.h @@ -0,0 +1,95 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#ifndef _WIN32_MSI +#define _WIN32_MSI 200 +#endif + +#define JET_VERSION 0x0501 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dutil.h" +#include "aclutil.h" +#include "atomutil.h" +#include "buffutil.h" +#include "butil.h" +#include "cabcutil.h" +#include "cabutil.h" +#include "conutil.h" +#include "cryputil.h" +#include "eseutil.h" +#include "dirutil.h" +#include "dlutil.h" +#include "fileutil.h" +#include "guidutil.h" +#include "gdiputil.h" +#include "dictutil.h" +#include "inetutil.h" +#include "iniutil.h" +#include "jsonutil.h" +#include "locutil.h" +#include "logutil.h" +#include "memutil.h" // NOTE: almost everying is inlined so there is a small .cpp file +//#include "metautil.h" - see metautil.cpp why this *must* be commented out +#include "monutil.h" +#include "osutil.h" +#include "pathutil.h" +#include "perfutil.h" +#include "polcutil.h" +#include "procutil.h" +#include "regutil.h" +#include "resrutil.h" +#include "reswutil.h" +#include "rmutil.h" +#include "rssutil.h" +#include "apuputil.h" // NOTE: this must come after atomutil.h and rssutil.h since it uses them. +#include "shelutil.h" +//#include "sqlutil.h" - see sqlutil.cpp why this *must* be commented out +#include "srputil.h" +#include "strutil.h" +#include "timeutil.h" +#include "timeutil.h" +#include "thmutil.h" +#include "uncutil.h" +#include "uriutil.h" +#include "userutil.h" +#include "varutil.h" +#include "condutil.h" // NOTE: This must come after varutil.h since it uses it. +#include "wiutil.h" +#include "wuautil.h" +#include // This header is needed for msxml2.h to compile correctly +#include // This file is needed to include xmlutil.h +#include "xmlutil.h" + diff --git a/src/dutil/proc2utl.cpp b/src/dutil/proc2utl.cpp new file mode 100644 index 00000000..8a2fd09b --- /dev/null +++ b/src/dutil/proc2utl.cpp @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** + ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcFindAllIdsFromExeName( + __in_z LPCWSTR wzExeName, + __out DWORD** ppdwProcessIds, + __out DWORD* pcProcessIds + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HANDLE hSnap = INVALID_HANDLE_VALUE; + BOOL fContinue = FALSE; + PROCESSENTRY32W peData = { sizeof(peData) }; + + hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (INVALID_HANDLE_VALUE == hSnap) + { + ExitWithLastError(hr, "Failed to create snapshot of processes on system"); + } + + fContinue = ::Process32FirstW(hSnap, &peData); + + while (fContinue) + { + if (0 == lstrcmpiW((LPCWSTR)&(peData.szExeFile), wzExeName)) + { + if (!*ppdwProcessIds) + { + *ppdwProcessIds = static_cast(MemAlloc(sizeof(DWORD), TRUE)); + ExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs."); + } + else + { + DWORD* pdwReAllocReturnedPids = NULL; + pdwReAllocReturnedPids = static_cast(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE)); + ExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs."); + + *ppdwProcessIds = pdwReAllocReturnedPids; + } + + (*ppdwProcessIds)[*pcProcessIds] = peData.th32ProcessID; + ++(*pcProcessIds); + } + + fContinue = ::Process32NextW(hSnap, &peData); + } + + er = ::GetLastError(); + if (ERROR_NO_MORE_FILES == er) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + ReleaseFile(hSnap); + + return hr; +} diff --git a/src/dutil/proc3utl.cpp b/src/dutil/proc3utl.cpp new file mode 100644 index 00000000..038f002b --- /dev/null +++ b/src/dutil/proc3utl.cpp @@ -0,0 +1,114 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT GetActiveSessionUserToken( + __out HANDLE *phToken + ); + + +/******************************************************************** + ProcExecuteAsInteractiveUser() - runs process as currently logged in + user. +*******************************************************************/ +extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser( + __in_z LPCWSTR wzExecutablePath, + __in_z LPCWSTR wzCommandLine, + __out HANDLE *phProcess + ) +{ + HRESULT hr = S_OK; + HANDLE hToken = NULL; + LPVOID pEnvironment = NULL; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + hr = GetActiveSessionUserToken(&hToken); + ExitOnFailure(hr, "Failed to get active session user token."); + + if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE)) + { + ExitWithLastError(hr, "Failed to create environment block for UI process."); + } + + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine); + ExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine); + } + + *phProcess = pi.hProcess; + pi.hProcess = NULL; + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseStr(sczFullCommandLine); + + if (pEnvironment) + { + ::DestroyEnvironmentBlock(pEnvironment); + } + + ReleaseHandle(hToken); + + return hr; +} + + +static HRESULT GetActiveSessionUserToken( + __out HANDLE *phToken + ) +{ + HRESULT hr = S_OK; + PWTS_SESSION_INFO pSessionInfo = NULL; + DWORD cSessions = 0; + DWORD dwSessionId = 0; + BOOL fSessionFound = FALSE; + HANDLE hToken = NULL; + + // Loop through the sessions looking for the active one. + if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions)) + { + ExitWithLastError(hr, "Failed to enumerate sessions."); + } + + for (DWORD i = 0; i < cSessions; ++i) + { + if (WTSActive == pSessionInfo[i].State) + { + dwSessionId = pSessionInfo[i].SessionId; + fSessionFound = TRUE; + + break; + } + } + + if (!fSessionFound) + { + ExitFunction1(hr = E_NOTFOUND); + } + + // Get the user token from the active session. + if (!::WTSQueryUserToken(dwSessionId, &hToken)) + { + ExitWithLastError(hr, "Failed to get active session user token."); + } + + *phToken = hToken; + hToken = NULL; + +LExit: + ReleaseHandle(hToken); + + if (pSessionInfo) + { + ::WTSFreeMemory(pSessionInfo); + } + + return hr; +} diff --git a/src/dutil/procutil.cpp b/src/dutil/procutil.cpp new file mode 100644 index 00000000..4b34c773 --- /dev/null +++ b/src/dutil/procutil.cpp @@ -0,0 +1,488 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private functions +static HRESULT CreatePipes( + __out HANDLE *phOutRead, + __out HANDLE *phOutWrite, + __out HANDLE *phErrWrite, + __out HANDLE *phInRead, + __out HANDLE *phInWrite + ); + +static BOOL CALLBACK CloseWindowEnumCallback( + __in HWND hWnd, + __in LPARAM lParam + ); + + +extern "C" HRESULT DAPI ProcElevated( + __in HANDLE hProcess, + __out BOOL* pfElevated + ) +{ + HRESULT hr = S_OK; + HANDLE hToken = NULL; + TOKEN_ELEVATION tokenElevated = { }; + DWORD cbToken = 0; + + if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + { + ExitWithLastError(hr, "Failed to open process token."); + } + + if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) + { + *pfElevated = (0 != tokenElevated.TokenIsElevated); + } + else + { + DWORD er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + + // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated. + if (E_INVALIDARG == hr) + { + *pfElevated = FALSE; + hr = S_OK; + } + else + { + ExitOnRootFailure(hr, "Failed to get elevation token from process."); + } + } + +LExit: + ReleaseHandle(hToken); + + return hr; +} + +extern "C" HRESULT DAPI ProcWow64( + __in HANDLE hProcess, + __out BOOL* pfWow64 + ) +{ + HRESULT hr = S_OK; + BOOL fIsWow64 = FALSE; + + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process"); + + if (pfnIsWow64Process) + { + if (!pfnIsWow64Process(hProcess, &fIsWow64)) + { + ExitWithLastError(hr, "Failed to check WOW64 process."); + } + } + + *pfWow64 = fIsWow64; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ) +{ + AssertSz(!pfsr->fDisabled, "File system redirection was already disabled."); + HRESULT hr = S_OK; + + typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *); + LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); + + if (!pfnWow64DisableWow64FsRedirection) + { + ExitFunction1(hr = E_NOTIMPL); + } + + if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState)) + { + ExitWithLastError(hr, "Failed to disable file system redirection."); + } + + pfsr->fDisabled = TRUE; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ) +{ + HRESULT hr = S_OK; + + if (pfsr->fDisabled) + { + typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID); + LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); + + if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState)) + { + ExitWithLastError(hr, "Failed to revert file system redirection."); + } + + pfsr->fDisabled = FALSE; + pfsr->pvRevertState = NULL; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI ProcExec( + __in_z LPCWSTR wzExecutablePath, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out HANDLE *phProcess + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L""); + ExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + si.wShowWindow = static_cast(nCmdShow); + if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine); + } + + *phProcess = pi.hProcess; + pi.hProcess = NULL; + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseStr(sczFullCommandLine); + + return hr; +} + + +/******************************************************************** + ProcExecute() - executes a command-line. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcExecute( + __in_z LPWSTR wzCommand, + __out HANDLE *phProcess, + __out_opt HANDLE *phChildStdIn, + __out_opt HANDLE *phChildStdOutErr + ) +{ + HRESULT hr = S_OK; + + PROCESS_INFORMATION pi = { }; + STARTUPINFOW si = { }; + + HANDLE hOutRead = INVALID_HANDLE_VALUE; + HANDLE hOutWrite = INVALID_HANDLE_VALUE; + HANDLE hErrWrite = INVALID_HANDLE_VALUE; + HANDLE hInRead = INVALID_HANDLE_VALUE; + HANDLE hInWrite = INVALID_HANDLE_VALUE; + + // Create redirect pipes. + hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); + ExitOnFailure(hr, "failed to create output pipes"); + + // Set up startup structure. + si.cb = sizeof(STARTUPINFOW); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = hInRead; + si.hStdOutput = hOutWrite; + si.hStdError = hErrWrite; + +#pragma prefast(push) +#pragma prefast(disable:25028) + if (::CreateProcessW(NULL, + wzCommand, // command line + NULL, // security info + NULL, // thread info + TRUE, // inherit handles + ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags + NULL, // environment + NULL, // cur dir + &si, + &pi)) +#pragma prefast(pop) + { + // Close child process output/input handles so child doesn't hang + // while waiting for input from parent process. + ::CloseHandle(hOutWrite); + hOutWrite = INVALID_HANDLE_VALUE; + + ::CloseHandle(hErrWrite); + hErrWrite = INVALID_HANDLE_VALUE; + + ::CloseHandle(hInRead); + hInRead = INVALID_HANDLE_VALUE; + } + else + { + ExitWithLastError(hr, "Process failed to execute."); + } + + *phProcess = pi.hProcess; + pi.hProcess = 0; + + if (phChildStdIn) + { + *phChildStdIn = hInWrite; + hInWrite = INVALID_HANDLE_VALUE; + } + + if (phChildStdOutErr) + { + *phChildStdOutErr = hOutRead; + hOutRead = INVALID_HANDLE_VALUE; + } + +LExit: + if (pi.hThread) + { + ::CloseHandle(pi.hThread); + } + + if (pi.hProcess) + { + ::CloseHandle(pi.hProcess); + } + + ReleaseFileHandle(hOutRead); + ReleaseFileHandle(hOutWrite); + ReleaseFileHandle(hErrWrite); + ReleaseFileHandle(hInRead); + ReleaseFileHandle(hInWrite); + + return hr; +} + + +/******************************************************************** + ProcWaitForCompletion() - waits for process to complete and gets return code. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcWaitForCompletion( + __in HANDLE hProcess, + __in DWORD dwTimeout, + __out DWORD *pReturnCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + // Wait for everything to finish + er = ::WaitForSingleObject(hProcess, dwTimeout); + if (WAIT_FAILED == er) + { + ExitWithLastError(hr, "Failed to wait for process to complete."); + } + else if (WAIT_TIMEOUT == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + + if (!::GetExitCodeProcess(hProcess, &er)) + { + ExitWithLastError(hr, "Failed to get process return code."); + } + + *pReturnCode = er; + +LExit: + return hr; +} + +/******************************************************************** + ProcWaitForIds() - waits for multiple processes to complete. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcWaitForIds( + __in_ecount(cProcessIds) const DWORD rgdwProcessIds[], + __in DWORD cProcessIds, + __in DWORD dwMilliseconds + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HANDLE hProcess = NULL; + HANDLE * rghProcesses = NULL; + DWORD cProcesses = 0; + + rghProcesses = static_cast(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); + ExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); + + for (DWORD i = 0; i < cProcessIds; ++i) + { + hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]); + if (hProcess != NULL) + { + rghProcesses[cProcesses++] = hProcess; + } + } + + er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); + if (WAIT_FAILED == er) + { + ExitWithLastError(hr, "Failed to wait for process to complete."); + } + else if (WAIT_TIMEOUT == er) + { + ExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); + } + +LExit: + if (rghProcesses) + { + for (DWORD i = 0; i < cProcesses; ++i) + { + if (NULL != rghProcesses[i]) + { + ::CloseHandle(rghProcesses[i]); + } + } + + MemFree(rghProcesses); + } + + return hr; +} + +/******************************************************************** + ProcCloseIds() - sends WM_CLOSE messages to all process ids. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcCloseIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cProcessIds; ++i) + { + if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i])) + { + ExitWithLastError(hr, "Failed to enumerate windows."); + } + } + +LExit: + return hr; +} + + +static HRESULT CreatePipes( + __out HANDLE *phOutRead, + __out HANDLE *phOutWrite, + __out HANDLE *phErrWrite, + __out HANDLE *phInRead, + __out HANDLE *phInWrite + ) +{ + HRESULT hr = S_OK; + SECURITY_ATTRIBUTES sa; + HANDLE hOutTemp = INVALID_HANDLE_VALUE; + HANDLE hInTemp = INVALID_HANDLE_VALUE; + + HANDLE hOutRead = INVALID_HANDLE_VALUE; + HANDLE hOutWrite = INVALID_HANDLE_VALUE; + HANDLE hErrWrite = INVALID_HANDLE_VALUE; + HANDLE hInRead = INVALID_HANDLE_VALUE; + HANDLE hInWrite = INVALID_HANDLE_VALUE; + + // Fill out security structure so we can inherit handles + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + // Create pipes + if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) + { + ExitWithLastError(hr, "failed to create output pipe"); + } + + if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) + { + ExitWithLastError(hr, "failed to create input pipe"); + } + + // Duplicate output pipe so standard error and standard output write to the same pipe. + if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "failed to duplicate write handle"); + } + + // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in + // the child process that can't be closed. + if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "failed to duplicate output pipe"); + } + + if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "failed to duplicate input pipe"); + } + + // now that everything has succeeded, assign to the outputs + *phOutRead = hOutRead; + hOutRead = INVALID_HANDLE_VALUE; + + *phOutWrite = hOutWrite; + hOutWrite = INVALID_HANDLE_VALUE; + + *phErrWrite = hErrWrite; + hErrWrite = INVALID_HANDLE_VALUE; + + *phInRead = hInRead; + hInRead = INVALID_HANDLE_VALUE; + + *phInWrite = hInWrite; + hInWrite = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hOutRead); + ReleaseFileHandle(hOutWrite); + ReleaseFileHandle(hErrWrite); + ReleaseFileHandle(hInRead); + ReleaseFileHandle(hInWrite); + ReleaseFileHandle(hOutTemp); + ReleaseFileHandle(hInTemp); + + return hr; +} + + +/******************************************************************** + CloseWindowEnumCallback() - outputs trace and log info + +*******************************************************************/ +static BOOL CALLBACK CloseWindowEnumCallback( + __in HWND hWnd, + __in LPARAM lParam + ) +{ + DWORD dwPid = static_cast(lParam); + DWORD dwProcessId = 0; + + ::GetWindowThreadProcessId(hWnd, &dwProcessId); + if (dwPid == dwProcessId) + { + ::SendMessageW(hWnd, WM_CLOSE, 0, 0); + } + + return TRUE; +} diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp new file mode 100644 index 00000000..ec370f58 --- /dev/null +++ b/src/dutil/regutil.cpp @@ -0,0 +1,926 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW; +static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW; +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL; +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL; +static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW; +static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW; +static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW; +static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW; +static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; +static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; +static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; + +static HMODULE vhAdvApi32Dll = NULL; +static BOOL vfRegInitialized = FALSE; + +/******************************************************************** + RegInitialize - initializes regutil + +*********************************************************************/ +extern "C" HRESULT DAPI RegInitialize( + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); + ExitOnFailure(hr, "Failed to load AdvApi32.dll"); + + // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW + vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); + + if (NULL == vpfnRegDeleteKeyExW) + { + vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; + } + + vfRegInitialized = TRUE; + +LExit: + return hr; +} + + +/******************************************************************** + RegUninitialize - uninitializes regutil + +*********************************************************************/ +extern "C" void DAPI RegUninitialize( + ) +{ + if (vhAdvApi32Dll) + { + ::FreeLibrary(vhAdvApi32Dll); + vhAdvApi32Dll = NULL; + vpfnRegDeleteKeyExWFromLibrary = NULL; + vpfnRegDeleteKeyExW = NULL; + } + + vfRegInitialized = FALSE; +} + + +/******************************************************************** + RegFunctionOverride - overrides the registry functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI RegFunctionOverride( + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW + ) +{ + vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; + vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW; + vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary; + vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW; + vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW; + vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW; + vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; + vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; + vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; +} + + +/******************************************************************** + RegCreate - creates a registry key. + +*********************************************************************/ +extern "C" HRESULT DAPI RegCreate( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); + ExitOnWin32Error(er, hr, "Failed to create registry key."); + +LExit: + return hr; +} + + +/******************************************************************** + RegCreate - creates a registry key with extra options. + +*********************************************************************/ +HRESULT DAPI RegCreateEx( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __in BOOL fVolatile, + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, + __out HKEY* phk, + __out_opt BOOL* pfCreated + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwDisposition; + + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition); + ExitOnWin32Error(er, hr, "Failed to create registry key."); + + if (pfCreated) + { + *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegOpen - opens a registry key. + +*********************************************************************/ +extern "C" HRESULT DAPI RegOpen( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to open registry key."); + +LExit: + return hr; +} + + +/******************************************************************** + RegDelete - deletes a registry key (and optionally it's whole tree). + +*********************************************************************/ +extern "C" HRESULT DAPI RegDelete( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fDeleteTree + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR pszEnumeratedSubKey = NULL; + LPWSTR pszRecursiveSubKey = NULL; + HKEY hkKey = NULL; + REGSAM samDesired = 0; + + if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); + } + + switch (kbKeyBitness) + { + case REG_KEY_32BIT: + samDesired = KEY_WOW64_32KEY; + break; + case REG_KEY_64BIT: + samDesired = KEY_WOW64_64KEY; + break; + case REG_KEY_DEFAULT: + // Nothing to do + break; + } + + if (fDeleteTree) + { + hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open this key for enumerating subkeys", wzSubKey); + + // Yes, keep enumerating the 0th item, because we're deleting it every time + while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) + { + ExitOnFailure(hr, "Failed to enumerate key 0"); + + hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); + ExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); + + hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); + ExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); + } + + hr = S_OK; + } + + if (NULL != vpfnRegDeleteKeyExW) + { + er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); + } + else + { + er = vpfnRegDeleteKeyW(hkRoot, wzSubKey); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to delete registry key."); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(pszEnumeratedSubKey); + ReleaseStr(pszRecursiveSubKey); + + return hr; +} + + +/******************************************************************** + RegKeyEnum - enumerates a registry key. + +*********************************************************************/ +extern "C" HRESULT DAPI RegKeyEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczKey + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD cch = 0; + + if (psczKey && *psczKey) + { + hr = StrMaxLength(*psczKey, reinterpret_cast(&cch)); + ExitOnFailure(hr, "Failed to determine length of string."); + } + + if (2 > cch) + { + cch = 2; + + hr = StrAlloc(psczKey, cch); + ExitOnFailure(hr, "Failed to allocate string to minimum size."); + } + + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); + if (ERROR_MORE_DATA == er) + { + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL); + ExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); + + ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator. + hr = StrAlloc(psczKey, cch); + ExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); + + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); + } + else if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = E_NOMOREITEMS); + } + ExitOnWin32Error(er, hr, "Failed to enum registry key."); + + // Always ensure the registry key name is null terminated. +#pragma prefast(push) +#pragma prefast(disable:26018) + (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works. +#pragma prefast(pop) + +LExit: + return hr; +} + + +/******************************************************************** + RegValueEnum - enumerates a registry value. + +*********************************************************************/ +HRESULT DAPI RegValueEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczName, + __out_opt DWORD *pdwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD cbValueName = 0; + + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL); + ExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); + + // Add one for null terminator + ++cbValueName; + + hr = StrAlloc(psczName, cbValueName); + ExitOnFailure(hr, "Failed to allocate array for registry value name"); + + er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = E_NOMOREITEMS); + } + ExitOnWin32Error(er, hr, "Failed to enumerate registry value"); + +LExit: + return hr; +} + +/******************************************************************** + RegGetType - reads a registry key value type. + *********************************************************************/ +HRESULT DAPI RegGetType( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD *pdwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to read registry value."); +LExit: + + return hr; +} + +/******************************************************************** + RegReadBinary - reads a registry key binary value. + NOTE: caller is responsible for freeing *ppbBuffer +*********************************************************************/ +HRESULT DAPI RegReadBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T *pcbBuffer + ) +{ + HRESULT hr = S_OK; + LPBYTE pbBuffer = NULL; + DWORD er = ERROR_SUCCESS; + DWORD cb = 0; + DWORD dwType = 0; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); + ExitOnWin32Error(er, hr, "Failed to get size of registry value."); + + // Zero-length binary values can exist + if (0 < cb) + { + pbBuffer = static_cast(MemAlloc(cb, FALSE)); + ExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to read registry value."); + } + + if (REG_BINARY == dwType) + { + *ppbBuffer = pbBuffer; + pbBuffer = NULL; + *pcbBuffer = cb; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + ExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseMem(pbBuffer); + + return hr; +} + + +/******************************************************************** + RegReadString - reads a registry key value as a string. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD cch = 0; + DWORD cb = 0; + DWORD dwType = 0; + LPWSTR sczExpand = NULL; + + if (psczValue && *psczValue) + { + hr = StrMaxLength(*psczValue, reinterpret_cast(&cch)); + ExitOnFailure(hr, "Failed to determine length of string."); + } + + if (2 > cch) + { + cch = 2; + + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string to minimum size."); + } + + cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); + if (ERROR_MORE_DATA == er) + { + cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string bigger for registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); + } + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to read registry key."); + + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) + { + // Always ensure the registry value is null terminated. + (*psczValue)[cch - 1] = L'\0'; + + if (REG_EXPAND_SZ == dwType) + { + hr = StrAllocString(&sczExpand, *psczValue, 0); + ExitOnFailure(hr, "Failed to copy registry value to expand."); + + hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); + ExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + ExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseStr(sczExpand); + + return hr; +} + + +/******************************************************************** + RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. + +*********************************************************************/ +HRESULT DAPI RegReadStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_ecount_opt(pcStrings) LPWSTR** prgsczStrings, + __out DWORD *pcStrings + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwNullCharacters = 0; + DWORD dwType = 0; + DWORD cb = 0; + DWORD cch = 0; + LPCWSTR wzSource = NULL; + LPWSTR sczValue = NULL; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); + if (0 < cb) + { + cch = cb / sizeof(WCHAR); + hr = StrAlloc(&sczValue, cch); + ExitOnFailure(hr, "Failed to allocate string for registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); + } + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to read registry key."); + + if (cb / sizeof(WCHAR) != cch) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); + } + + if (REG_MULTI_SZ != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + ExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); + } + + // Value exists, but is empty, so no strings to return. + if (2 > cch) + { + *prgsczStrings = NULL; + *pcStrings = 0; + ExitFunction1(hr = S_OK); + } + + // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. + if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); + } + + cch = cb / sizeof(WCHAR); + for (DWORD i = 0; i < cch; ++i) + { + if (L'\0' == sczValue[i]) + { + ++dwNullCharacters; + } + } + + // There's one string for every null character encountered (except the extra 1 at the end of the string) + *pcStrings = dwNullCharacters - 1; + hr = MemEnsureArraySize(reinterpret_cast(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); + ExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); + +#pragma prefast(push) +#pragma prefast(disable:26010) + wzSource = sczValue; + for (DWORD i = 0; i < *pcStrings; ++i) + { + hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); + ExitOnFailure(hr, "Failed to allocate copy of string"); + + // Skip past this string + wzSource += lstrlenW(wzSource) + 1; + } +#pragma prefast(pop) + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + +/******************************************************************** + RegReadVersion - reads a registry key value as a version. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pdw64Version + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = 0; + LPWSTR sczVersion = NULL; + + cb = sizeof(DWORD64); + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*pdw64Version), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) + { + hr = RegReadString(hk, wzName, &sczVersion); + ExitOnFailure(hr, "Failed to read registry version as string."); + + hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); + ExitOnFailure(hr, "Failed to convert registry string to version."); + } + else if (REG_QWORD == dwType) + { + ExitOnWin32Error(er, hr, "Failed to read registry key."); + } + else // unexpected data type + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseStr(sczVersion); + + return hr; +} + + +/******************************************************************** + RegReadNumber - reads a DWORD registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = sizeof(DWORD); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pdwValue), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + + if (REG_DWORD != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegReadQword - reads a QWORD registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pqwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = sizeof(DWORD64); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pqwValue), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + + if (REG_QWORD != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegWriteBinary - writes a registry key value as a binary. + +*********************************************************************/ +HRESULT DAPI RegWriteBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_bcount(cbBuffer) const BYTE *pbBuffer, + __in DWORD cbBuffer + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer); + ExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); + +LExit: + return hr; +} + + +/******************************************************************** + RegWriteString - writes a registry key value as a string. + + Note: if wzValue is NULL the value will be removed. +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD cbValue = 0; + + if (wzValue) + { + hr = ::StringCbLengthW(wzValue, DWORD_MAX, reinterpret_cast(&cbValue)); + ExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_SZ, reinterpret_cast(wzValue), cbValue); + ExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); + } + else + { + er = vpfnRegDeleteValueW(hk, wzName); + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegWriteStringFormatted - writes a registry key value as a formatted string. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteStringFormatted( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in __format_string LPCWSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + va_list args; + + va_start(args, szFormat); + hr = StrAllocFormattedArgs(&sczValue, szFormat, args); + va_end(args); + ExitOnFailure(hr, "Failed to allocate %ls value.", wzName); + + hr = RegWriteString(hk, wzName, sczValue); + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + +/******************************************************************** + RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value + +*********************************************************************/ +HRESULT DAPI RegWriteStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_ecount(cValues) LPWSTR *rgwzValues, + __in DWORD cValues + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR wzCopyDestination = NULL; + LPCWSTR wzWriteValue = NULL; + LPWSTR sczWriteValue = NULL; + DWORD dwTotalStringSize = 0; + DWORD cbTotalStringSize = 0; + DWORD dwTemp = 0; + + if (0 == cValues) + { + wzWriteValue = L"\0"; + } + else + { + // Add space for the null terminator + dwTotalStringSize = 1; + + for (DWORD i = 0; i < cValues; ++i) + { + dwTemp = dwTotalStringSize; + hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize); + ExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); + } + + hr = StrAlloc(&sczWriteValue, dwTotalStringSize); + ExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); + + wzCopyDestination = sczWriteValue; + dwTemp = dwTotalStringSize; + for (DWORD i = 0; i < cValues; ++i) + { + hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); + ExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); + + dwTemp -= lstrlenW(rgwzValues[i]) + 1; + wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; + } + + wzWriteValue = sczWriteValue; + } + + hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); + ExitOnFailure(hr, "Failed to get total string size in bytes"); + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast(wzWriteValue), cbTotalStringSize); + ExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); + +LExit: + ReleaseStr(sczWriteValue); + + return hr; +} + +/******************************************************************** + RegWriteNumber - writes a registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof(dwValue)); + ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + +LExit: + return hr; +} + +/******************************************************************** + RegWriteQword - writes a registry key value as a Qword. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast(&qwValue), sizeof(qwValue)); + ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + +LExit: + return hr; +} + +/******************************************************************** + RegQueryKey - queries the key for the number of subkeys and values. + +*********************************************************************/ +extern "C" HRESULT DAPI RegQueryKey( + __in HKEY hk, + __out_opt DWORD* pcSubKeys, + __out_opt DWORD* pcValues + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL); + ExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); + +LExit: + return hr; +} diff --git a/src/dutil/resrutil.cpp b/src/dutil/resrutil.cpp new file mode 100644 index 00000000..1da03ed9 --- /dev/null +++ b/src/dutil/resrutil.cpp @@ -0,0 +1,251 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define RES_STRINGS_PER_BLOCK 16 + + +BOOL CALLBACK EnumLangIdProc( + __in_opt HMODULE hModule, + __in_z LPCSTR lpType, + __in_z LPCSTR lpName, + __in WORD wLanguage, + __in LONG_PTR lParam + ); + +/******************************************************************** +ResGetStringLangId - get the language id for a string in the string table. + +********************************************************************/ +extern "C" HRESULT DAPI ResGetStringLangId( + __in_opt LPCWSTR wzPath, + __in UINT uID, + __out WORD *pwLangId + ) +{ + Assert(pwLangId); + + HRESULT hr = S_OK; + HINSTANCE hModule = NULL; + DWORD dwBlockId = (uID / RES_STRINGS_PER_BLOCK) + 1; + WORD wFoundLangId = 0; + + if (wzPath && *wzPath) + { + hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); + ExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath); + } + +#pragma prefast(push) +#pragma prefast(disable:25068) + if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast(EnumLangIdProc), reinterpret_cast(&wFoundLangId))) +#pragma prefast(pop) + { + ExitWithLastError(hr, "Failed to find string language identifier."); + } + + *pwLangId = wFoundLangId; + +LExit: + if (hModule) + { + ::FreeLibrary(hModule); + } + + return hr; +} + + +/******************************************************************** +ResReadString + +NOTE: ppwzString should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI ResReadString( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPWSTR* ppwzString + ) +{ + Assert(hinst && ppwzString); + + HRESULT hr = S_OK; + DWORD cch = 64; // first guess + DWORD cchReturned = 0; + + do + { + hr = StrAlloc(ppwzString, cch); + ExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + + cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch); + if (0 == cchReturned) + { + ExitWithLastError(hr, "Failed to load string resource id: %d", uID); + } + + // if the returned string count is one character too small, it's likely we have + // more data to read + if (cchReturned + 1 == cch) + { + cch *= 2; + hr = S_FALSE; + } + } while (S_FALSE == hr); + ExitOnFailure(hr, "Failed to load string resource id: %d", uID); + +LExit: + return hr; +} + + +/******************************************************************** + ResReadStringAnsi + + NOTE: ppszString should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI ResReadStringAnsi( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPSTR* ppszString + ) +{ + Assert(hinst && ppszString); + + HRESULT hr = S_OK; + DWORD cch = 64; // first guess + DWORD cchReturned = 0; + + do + { + hr = StrAnsiAlloc(ppszString, cch); + ExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + +#pragma prefast(push) +#pragma prefast(disable:25068) + cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch); +#pragma prefast(pop) + if (0 == cchReturned) + { + ExitWithLastError(hr, "Failed to load string resource id: %d", uID); + } + + // if the returned string count is one character too small, it's likely we have + // more data to read + if (cchReturned + 1 == cch) + { + cch *= 2; + hr = S_FALSE; + } + } while (S_FALSE == hr); + ExitOnFailure(hr, "failed to load string resource id: %d", uID); + +LExit: + return hr; +} + + +/******************************************************************** +ResReadData - returns a pointer to the specified resource data + +NOTE: there is no "free" function for this call +********************************************************************/ +extern "C" HRESULT DAPI ResReadData( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szDataName, + __deref_out_bcount(*pcb) PVOID *ppv, + __out DWORD *pcb + ) +{ + Assert(szDataName); + Assert(ppv); + + HRESULT hr = S_OK; + HRSRC hRsrc = NULL; + HGLOBAL hData = NULL; + DWORD cbData = 0; + +#pragma prefast(push) +#pragma prefast(disable:25068) + hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); +#pragma prefast(pop) + ExitOnNullWithLastError(hRsrc, hr, "Failed to find resource."); + + hData = ::LoadResource(hinst, hRsrc); + ExitOnNullWithLastError(hData, hr, "Failed to load resource."); + + cbData = ::SizeofResource(hinst, hRsrc); + if (!cbData) + { + ExitWithLastError(hr, "Failed to get size of resource."); + } + + *ppv = ::LockResource(hData); + ExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource."); + *pcb = cbData; + +LExit: + return hr; +} + + +/******************************************************************** +ResExportDataToFile - extracts the resource data to the specified target file + +********************************************************************/ +extern "C" HRESULT DAPI ResExportDataToFile( + __in_z LPCSTR szDataName, + __in_z LPCWSTR wzTargetFile, + __in DWORD dwCreationDisposition + ) +{ + HRESULT hr = S_OK; + PVOID pData = NULL; + DWORD cbData = 0; + DWORD cbWritten = 0; + HANDLE hFile = INVALID_HANDLE_VALUE; + BOOL bCreatedFile = FALSE; + + hr = ResReadData(NULL, szDataName, &pData, &cbData); + ExitOnFailure(hr, "Failed to GetData from %s.", szDataName); + + hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile); + } + bCreatedFile = TRUE; + + if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL)) + { + ExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile); + } + +LExit: + ReleaseFile(hFile); + + if (FAILED(hr)) + { + if (bCreatedFile) + { + ::DeleteFileW(wzTargetFile); + } + } + + return hr; +} + + +BOOL CALLBACK EnumLangIdProc( + __in_opt HMODULE /* hModule */, + __in_z LPCSTR /* lpType */, + __in_z LPCSTR /* lpName */, + __in WORD wLanguage, + __in LONG_PTR lParam + ) +{ + WORD *pwLangId = reinterpret_cast(lParam); + + *pwLangId = wLanguage; + return TRUE; +} diff --git a/src/dutil/reswutil.cpp b/src/dutil/reswutil.cpp new file mode 100644 index 00000000..e534fc09 --- /dev/null +++ b/src/dutil/reswutil.cpp @@ -0,0 +1,368 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define RES_STRINGS_PER_BLOCK 16 + +// Internal data structure format for a string block in a resource table. +// Note: Strings are always stored as UNICODED. +typedef struct _RES_STRING_BLOCK +{ + DWORD dwBlockId; + WORD wLangId; + LPWSTR rgwz[RES_STRINGS_PER_BLOCK]; +} RES_STRING_BLOCK; + + +// private functions +static HRESULT StringBlockInitialize( + __in_opt HINSTANCE hModule, + __in DWORD dwBlockId, + __in WORD wLangId, + __in RES_STRING_BLOCK* pStrBlock + ); +static void StringBlockUnitialize( + __in RES_STRING_BLOCK* pStrBlock + ); +static HRESULT StringBlockChangeString( + __in RES_STRING_BLOCK* pStrBlock, + __in DWORD dwStringId, + __in_z LPCWSTR szData + ); +static HRESULT StringBlockConvertToResourceData( + __in const RES_STRING_BLOCK* pStrBlock, + __deref_out_bcount(*pcbData) LPVOID* ppvData, + __out DWORD* pcbData + ); +static HRESULT StringBlockConvertFromResourceData( + __in RES_STRING_BLOCK* pStrBlock, + __in_bcount(cbData) LPCVOID pvData, + __in SIZE_T cbData + ); + + +/******************************************************************** +ResWriteString - sets the string into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResWriteString( + __in_z LPCWSTR wzResourceFile, + __in DWORD dwDataId, + __in_z LPCWSTR wzData, + __in WORD wLangId + ) +{ + Assert(wzResourceFile); + Assert(wzData); + + HRESULT hr = S_OK; + HINSTANCE hModule = NULL; + HANDLE hUpdate = NULL; + RES_STRING_BLOCK StrBlock = { }; + LPVOID pvData = NULL; + DWORD cbData = 0; + + DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1; + DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK); + + hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); + ExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); + + hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock); + ExitOnFailure(hr, "Failed to get string block to update."); + + hr = StringBlockChangeString(&StrBlock, dwStringId, wzData); + ExitOnFailure(hr, "Failed to update string block string."); + + hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData); + ExitOnFailure(hr, "Failed to convert string block to resource data."); + + ::FreeLibrary(hModule); + hModule = NULL; + + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); + ExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + + if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData)) + { + ExitWithLastError(hr, "Failed to ::UpdateResourceA."); + } + + if (!::EndUpdateResource(hUpdate, FALSE)) + { + ExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + } + + hUpdate = NULL; + +LExit: + ReleaseMem(pvData); + + StringBlockUnitialize(&StrBlock); + + if (hUpdate) + { + ::EndUpdateResource(hUpdate, TRUE); + } + + if (hModule) + { + ::FreeLibrary(hModule); + } + + return hr; +} + + +/******************************************************************** +ResWriteData - sets the data into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResWriteData( + __in_z LPCWSTR wzResourceFile, + __in_z LPCSTR szDataName, + __in PVOID pData, + __in DWORD cbData + ) +{ + Assert(wzResourceFile); + Assert(szDataName); + Assert(pData); + Assert(cbData); + + HRESULT hr = S_OK; + HANDLE hUpdate = NULL; + + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); + ExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + + if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData)) + { + ExitWithLastError(hr, "Failed to ::UpdateResourceA."); + } + + if (!::EndUpdateResource(hUpdate, FALSE)) + { + ExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + } + + hUpdate = NULL; + +LExit: + if (hUpdate) + { + ::EndUpdateResource(hUpdate, TRUE); + } + + return hr; +} + + +/******************************************************************** +ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResImportDataFromFile( + __in_z LPCWSTR wzTargetFile, + __in_z LPCWSTR wzSourceFile, + __in_z LPCSTR szDataName + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD cbFile = 0; + HANDLE hMap = NULL; + PVOID pv = NULL; + + hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); + } + + cbFile = ::GetFileSize(hFile, NULL); + if (!cbFile) + { + ExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); + } + + hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + ExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); + + pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile); + ExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); + + hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile); + ExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); + +LExit: + if (pv) + { + ::UnmapViewOfFile(pv); + } + + if (hMap) + { + ::CloseHandle(hMap); + } + + ReleaseFile(hFile); + + return hr; +} + + +static HRESULT StringBlockInitialize( + __in_opt HINSTANCE hModule, + __in DWORD dwBlockId, + __in WORD wLangId, + __in RES_STRING_BLOCK* pStrBlock + ) +{ + HRESULT hr = S_OK; + HRSRC hRsrc = NULL; + HGLOBAL hData = NULL; + LPCVOID pvData = NULL; // does not need to be freed + DWORD cbData = 0; + + hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId); + ExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); + + hData = ::LoadResource(hModule, hRsrc); + ExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); + + cbData = ::SizeofResource(hModule, hRsrc); + if (!cbData) + { + ExitWithLastError(hr, "Failed to ::SizeofResource."); + } + + pvData = ::LockResource(hData); + ExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); + + pStrBlock->dwBlockId = dwBlockId; + pStrBlock->wLangId = wLangId; + + hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData); + ExitOnFailure(hr, "Failed to convert string block from resource data."); + +LExit: + return hr; +} + + +static void StringBlockUnitialize( + __in RES_STRING_BLOCK* pStrBlock + ) +{ + if (pStrBlock) + { + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + ReleaseNullMem(pStrBlock->rgwz[i]); + } + } +} + + +static HRESULT StringBlockChangeString( + __in RES_STRING_BLOCK* pStrBlock, + __in DWORD dwStringId, + __in_z LPCWSTR szData + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzData = NULL; + DWORD cchData = lstrlenW(szData); + + pwzData = static_cast(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); + ExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); + + hr = ::StringCchCopyW(pwzData, cchData + 1, szData); + ExitOnFailure(hr, "Failed to copy new block string."); + + ReleaseNullMem(pStrBlock->rgwz[dwStringId]); + + pStrBlock->rgwz[dwStringId] = pwzData; + pwzData = NULL; + +LExit: + ReleaseMem(pwzData); + + return hr; +} + + +static HRESULT StringBlockConvertToResourceData( + __in const RES_STRING_BLOCK* pStrBlock, + __deref_out_bcount(*pcbData) LPVOID* ppvData, + __out DWORD* pcbData + ) +{ + HRESULT hr = S_OK; + DWORD cbData = 0; + LPVOID pvData = NULL; + WCHAR* pwz = NULL; + + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1); + } + cbData *= sizeof(WCHAR); + + pvData = MemAlloc(cbData, TRUE); + ExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); + + pwz = static_cast(pvData); + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + DWORD cch = lstrlenW(pStrBlock->rgwz[i]); + + *pwz = static_cast(cch); + ++pwz; + + for (DWORD j = 0; j < cch; ++j) + { + *pwz = pStrBlock->rgwz[i][j]; + ++pwz; + } + } + + *pcbData = cbData; + *ppvData = pvData; + pvData = NULL; + +LExit: + ReleaseMem(pvData); + + return hr; +} + + +static HRESULT StringBlockConvertFromResourceData( + __in RES_STRING_BLOCK* pStrBlock, + __in_bcount(cbData) LPCVOID pvData, + __in SIZE_T cbData + ) +{ + UNREFERENCED_PARAMETER(cbData); + HRESULT hr = S_OK; + LPCWSTR pwzParse = static_cast(pvData); + + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + DWORD cchParse = static_cast(*pwzParse); + ++pwzParse; + + pStrBlock->rgwz[i] = static_cast(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE)); + ExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); + + hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ExitOnFailure(hr, "Failed to copy parsed resource data into string block."); + + pwzParse += cchParse; + } + +LExit: + return hr; +} diff --git a/src/dutil/rexutil.cpp b/src/dutil/rexutil.cpp new file mode 100644 index 00000000..73500630 --- /dev/null +++ b/src/dutil/rexutil.cpp @@ -0,0 +1,586 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include "rexutil.h" + +// +// static globals +// +static HMODULE vhCabinetDll = NULL; +static HFDI vhfdi = NULL; +static ERF verf; + +static FAKE_FILE vrgffFileTable[FILETABLESIZE]; +static DWORD vcbRes; +static LPCBYTE vpbRes; +static CHAR vszResource[MAX_PATH]; +static REX_CALLBACK_WRITE vpfnWrite = NULL; + +static HRESULT vhrLastError = S_OK; + +// +// structs +// +struct REX_CALLBACK_STRUCT +{ + BOOL fStopExtracting; // flag set when no more files are needed + LPCWSTR pwzExtract; // file to extract ("*" means extract all) + LPCWSTR pwzExtractDir; // directory to extract files to + LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*") + + // possible user data + REX_CALLBACK_PROGRESS pfnProgress; + LPVOID pvContext; +}; + +// +// prototypes +// +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize); +static __callback void DIAMONDAPI RexFree(LPVOID pvData); +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode); +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb); +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb); +static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf); +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype); +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify); + + +/******************************************************************** + RexInitialize - initializes internal static variables + +*******************************************************************/ +extern "C" HRESULT RexInitialize() +{ + Assert(!vhfdi); + + HRESULT hr = S_OK; + + vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf); + if (!vhfdi) + { + hr = E_FAIL; + ExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here + } + + ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable)); + +LExit: + if (FAILED(hr)) + { + ::FDIDestroy(vhfdi); + vhfdi = NULL; + } + + return hr; +} + + +/******************************************************************** + RexUninitialize - initializes internal static variables + +*******************************************************************/ +extern "C" void RexUninitialize() +{ + if (vhfdi) + { + ::FDIDestroy(vhfdi); + vhfdi = NULL; + } +} + + +/******************************************************************** + RexExtract - extracts one or all files from a resource cabinet + + NOTE: wzExtractId can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + wzExtractName is ignored if wzExtractId is "*" +*******************************************************************/ +extern "C" HRESULT RexExtract( + __in_z LPCSTR szResource, + __in_z LPCWSTR wzExtractId, + __in_z LPCWSTR wzExtractDir, + __in_z LPCWSTR wzExtractName, + __in REX_CALLBACK_PROGRESS pfnProgress, + __in REX_CALLBACK_WRITE pfnWrite, + __in LPVOID pvContext + ) +{ + Assert(vhfdi); + HRESULT hr = S_OK; + BOOL fResult; + + HRSRC hResInfo = NULL; + HANDLE hRes = NULL; + + REX_CALLBACK_STRUCT rcs; + + // remember the write callback + vpfnWrite = pfnWrite; + + // + // load the cabinet resource + // + hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + ExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); + //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10)); + //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info"); + + hRes = ::LoadResource(NULL, hResInfo); + ExitOnNullWithLastError(hRes, hr, "failed to load resource"); + + vcbRes = ::SizeofResource(NULL, hResInfo); + vpbRes = (const BYTE*)::LockResource(hRes); + + // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it + + // + // convert the resource name to multi-byte + // + //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL)) + //{ + // ExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); + //} + + hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); + ExitOnFailure(hr, "Failed to copy resource name to global."); + + // + // iterate through files in cabinet extracting them to the callback function + // + rcs.fStopExtracting = FALSE; + rcs.pwzExtract = wzExtractId; + rcs.pwzExtractDir = wzExtractDir; + rcs.pwzExtractName = wzExtractName; + rcs.pfnProgress = pfnProgress; + rcs.pvContext = pvContext; + + fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast(&rcs)); + if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure + { + hr = vhrLastError; // TODO: put verf info in trace message here + } + +LExit: + return hr; +} + + +/**************************************************************************** + default extract routines + +****************************************************************************/ +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize) +{ + return MemAlloc(dwSize, FALSE); +} + + +static __callback void DIAMONDAPI RexFree(LPVOID pvData) +{ + MemFree(pvData); +} + + +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + int i = 0; + + // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); + } + + // find an empty spot in the fake file table + for (i = 0; i < FILETABLESIZE; ++i) + { + if (!vrgffFileTable[i].fUsed) + { + break; + } + } + + // we should never run out of space in the fake file table + if (FILETABLESIZE <= i) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "File table exceeded"); + } + + if (0 == lstrcmpA(vszResource, pszFile)) + { + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = MEMORY_FILE; + vrgffFileTable[i].mfFile.vpStart = static_cast(vpbRes); + vrgffFileTable[i].mfFile.uiCurrent = 0; + vrgffFileTable[i].mfFile.uiLength = vcbRes; + } + else // it's a real file + { + hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "failed to open file: %s", pszFile); + } + + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = NORMAL_FILE; + vrgffFileTable[i].hFile = hFile; + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : i; +} + + +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + DWORD cbRead = 0; + DWORD cbAvailable = 0; + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + // ensure that we don't read past the length of the resource + cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent; + cbRead = cb < cbAvailable? cb : cbAvailable; + + memcpy(pv, static_cast(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead); + + vrgffFileTable[hf].mfFile.uiCurrent += cbRead; + } + else // NORMAL_FILE + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL)) + { + ExitWithLastError(hr, "failed to read during cabinet extraction"); + } + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb) +{ + Assert(vrgffFileTable[hf].fUsed); + Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file + + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + if (!::WriteFile(reinterpret_cast(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL)) + { + ExitWithLastError(hr, "failed to write during cabinet extraction"); + } + + // call the writer callback if defined + if (vpfnWrite) + { + vpfnWrite(cb); + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + ExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + } + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + if (FILE_BEGIN == dwMoveMethod) + { + vrgffFileTable[hf].mfFile.uiCurrent = dist; + } + else if (FILE_CURRENT == dwMoveMethod) + { + vrgffFileTable[hf].mfFile.uiCurrent += dist; + } + else // FILE_END + { + vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist; + } + + lMove = vrgffFileTable[hf].mfFile.uiCurrent; + } + else // NORMAL_FILE + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod); + if (0xFFFFFFFF == lMove) + { + ExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + } + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : lMove; +} + + +__callback int FAR DIAMONDAPI RexClose(INT_PTR hf) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + vrgffFileTable[hf].mfFile.vpStart = NULL; + vrgffFileTable[hf].mfFile.uiCurrent = 0; + vrgffFileTable[hf].mfFile.uiLength = 0; + } + else + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + if (!::CloseHandle(vrgffFileTable[hf].hFile)) + { + ExitWithLastError(hr, "failed to close file during cabinet extraction"); + } + + vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE; + } + + vrgffFileTable[hf].fUsed = FALSE; + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : 0; +} + + +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify) +{ + Assert(pFDINotify->pv); + + HRESULT hr = S_OK; + int ipResult = 0; // result to return on success + HANDLE hFile = INVALID_HANDLE_VALUE; + + REX_CALLBACK_STRUCT* prcs = static_cast(pFDINotify->pv); + LPCSTR sz; + WCHAR wz[MAX_PATH]; + FILETIME ft; + int i = 0; + + switch (iNotification) + { + case fdintCOPY_FILE: // beGIN extracting a resource from cabinet + Assert(pFDINotify->psz1); + + if (prcs->fStopExtracting) + { + ExitFunction1(hr = S_FALSE); // no more extracting + } + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (prcs->pfnProgress) + { + hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); + if (S_OK != hr) + { + ExitFunction(); + } + } + + if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) + { + // get the created date for the resource in the cabinet + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) + { + ExitWithLastError(hr, "failed to get time for resource: %ls", wz); + } + + WCHAR wzPath[MAX_PATH]; + + hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); + ExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); + + if (L'*' == *prcs->pwzExtract) + { + hr = ::StringCchCatW(wzPath, countof(wzPath), wz); + ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + } + else + { + Assert(*prcs->pwzExtractName); + + hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); + ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); + } + + // Quickly chop off the file name part of the path to ensure the path exists + // then put the file name back on the path (by putting the first character + // back over the null terminator). + LPWSTR wzFile = PathFile(wzPath); + WCHAR wzFileFirstChar = *wzFile; + *wzFile = L'\0'; + + hr = DirEnsureExists(wzPath, NULL); + ExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); + + hr = S_OK; + + *wzFile = wzFileFirstChar; + + // find an empty spot in the fake file table + for (i = 0; i < FILETABLESIZE; ++i) + { + if (!vrgffFileTable[i].fUsed) + { + break; + } + } + + // we should never run out of space in the fake file table + if (FILETABLESIZE <= i) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "File table exceeded"); + } + + // open the file + hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "failed to open file: %ls", wzPath); + } + + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = NORMAL_FILE; + vrgffFileTable[i].hFile = hFile; + + ipResult = i; + + ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) + + if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) + { + if (::SetEndOfFile(vrgffFileTable[i].hFile)) + { + ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer + } + } + } + else // resource wasn't requested, skip it + { + hr = S_OK; + ipResult = 0; + } + + break; + case fdintCLOSE_FILE_INFO: // resource extraction complete + Assert(pFDINotify->hf && pFDINotify->psz1); + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + RexClose(pFDINotify->hf); + + if (prcs->pfnProgress) + { + hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); + } + + if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going + { + ipResult = TRUE; + } + else // something went wrong or we only needed to extract one file + { + hr = S_OK; + ipResult = FALSE; + prcs->fStopExtracting = TRUE; + } + + break; + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + default: + AssertSz(FALSE, "RexCallback() - unknown FDI notification command"); + }; + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return (S_OK == hr) ? ipResult : -1; +} diff --git a/src/dutil/rmutil.cpp b/src/dutil/rmutil.cpp new file mode 100644 index 00000000..75d3e277 --- /dev/null +++ b/src/dutil/rmutil.cpp @@ -0,0 +1,473 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include + +#define ARRAY_GROWTH_SIZE 5 + +typedef DWORD (WINAPI *PFNRMJOINSESSION)( + __out DWORD *pSessionHandle, + __in_z const WCHAR strSessionKey[] + ); + +typedef DWORD (WINAPI *PFNRMENDSESSION)( + __in DWORD dwSessionHandle + ); + +typedef DWORD (WINAPI *PFNRMREGISTERRESOURCES)( + __in DWORD dwSessionHandle, + __in UINT nFiles, + __in_z_opt LPWSTR *rgsFilenames, + __in UINT nApplications, + __in_opt RM_UNIQUE_PROCESS *rgApplications, + __in UINT nServices, + __in_z_opt LPWSTR *rgsServiceNames + ); + +typedef struct _RMU_SESSION +{ + CRITICAL_SECTION cs; + DWORD dwSessionHandle; + BOOL fStartedSessionHandle; + BOOL fInitialized; + + UINT cFilenames; + LPWSTR *rgsczFilenames; + + UINT cApplications; + RM_UNIQUE_PROCESS *rgApplications; + + UINT cServiceNames; + LPWSTR *rgsczServiceNames; + +} RMU_SESSION; + +static volatile LONG vcRmuInitialized = 0; +static HMODULE vhModule = NULL; +static PFNRMJOINSESSION vpfnRmJoinSession = NULL; +static PFNRMENDSESSION vpfnRmEndSession = NULL; +static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL; + +static HRESULT RmuInitialize(); +static void RmuUninitialize(); + +static HRESULT RmuApplicationArrayAlloc( + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, + __inout LPUINT pcApplications, + __in DWORD dwProcessId, + __in FILETIME ProcessStartTime + ); + +static HRESULT RmuApplicationArrayFree( + __in RM_UNIQUE_PROCESS *rgApplications + ); + +#define ReleaseNullApplicationArray(rg, c) { if (rg) { RmuApplicationArrayFree(rg); c = 0; rg = NULL; } } + +/******************************************************************** +RmuJoinSession - Joins an existing Restart Manager session. + +********************************************************************/ +extern "C" HRESULT DAPI RmuJoinSession( + __out PRMU_SESSION *ppSession, + __in_z LPCWSTR wzSessionKey + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + PRMU_SESSION pSession = NULL; + + *ppSession = NULL; + + pSession = static_cast(MemAlloc(sizeof(RMU_SESSION), TRUE)); + ExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); + + hr = RmuInitialize(); + ExitOnFailure(hr, "Failed to initialize Restart Manager."); + + er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey); + ExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); + + ::InitializeCriticalSection(&pSession->cs); + pSession->fInitialized = TRUE; + + *ppSession = pSession; + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(pSession); + } + + return hr; +} + +/******************************************************************** +RmuAddFile - Adds the file path to the Restart Manager session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddFile( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pSession->cs); + + // Create or grow the jagged array. + hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0); + ExitOnFailure(hr, "Failed to add the filename to the array."); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuAddProcessById - Adds the process ID to the Restart Manager sesion. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddProcessById( + __in PRMU_SESSION pSession, + __in DWORD dwProcessId + ) +{ + HRESULT hr = S_OK; + HANDLE hProcess = NULL; + FILETIME CreationTime = {}; + FILETIME ExitTime = {}; + FILETIME KernelTime = {}; + FILETIME UserTime = {}; + BOOL fLocked = FALSE; + + HANDLE hToken = NULL; + TOKEN_PRIVILEGES priv = { 0 }; + TOKEN_PRIVILEGES* pPrevPriv = NULL; + DWORD cbPrevPriv = 0; + DWORD er = ERROR_SUCCESS; + BOOL fAdjustedPrivileges = FALSE; + BOOL fElevated = FALSE; + ProcElevated(::GetCurrentProcess(), &fElevated); + + // Must be elevated to adjust process privileges + if (fElevated) { + // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context. + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + ExitWithLastError(hr, "Failed to get process token."); + } + + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) + { + ExitWithLastError(hr, "Failed to get debug privilege LUID."); + } + + cbPrevPriv = sizeof(TOKEN_PRIVILEGES); + pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); + ExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); + + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + { + LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); + pPrevPriv = static_cast(pv); + + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + { + ExitWithLastError(hr, "Failed to get debug privilege LUID."); + } + } + + fAdjustedPrivileges = TRUE; + } + + hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); + if (hProcess) + { + if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime)) + { + ExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId); + } + + ::EnterCriticalSection(&pSession->cs); + fLocked = TRUE; + hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime); + ExitOnFailure(hr, "Failed to add the application to the array."); + } + else + { + er = ::GetLastError(); + if (ERROR_ACCESS_DENIED == er) + { + // OpenProcess will fail when not elevated and the target process is in another user context. Let the caller log and continue. + hr = E_NOTFOUND; + } + else + { + ExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId); + } + } + +LExit: + if (hProcess) + { + ::CloseHandle(hProcess); + } + + if (fAdjustedPrivileges) + { + ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL); + } + + ReleaseMem(pPrevPriv); + ReleaseHandle(hToken); + + if (fLocked) + { + ::LeaveCriticalSection(&pSession->cs); + } + + return hr; +} + +/******************************************************************** +RmuAddProcessesByName - Adds all processes by the given process name + to the Restart Manager Session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddProcessesByName( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzProcessName + ) +{ + HRESULT hr = S_OK; + DWORD *pdwProcessIds = NULL; + DWORD cProcessIds = 0; + BOOL fNotFound = FALSE; + + hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds); + ExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName); + + for (DWORD i = 0; i < cProcessIds; ++i) + { + hr = RmuAddProcessById(pSession, pdwProcessIds[i]); + if (E_NOTFOUND == hr) + { + // RmuAddProcessById returns E_NOTFOUND when this setup is not elevated and OpenProcess returned access denied (target process running under another user account). + fNotFound = TRUE; + } + else + { + ExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]); + } + } + + // 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. + if (fNotFound) + { + hr = E_NOTFOUND; + } + +LExit: + ReleaseMem(pdwProcessIds); + + return hr; +} + +/******************************************************************** +RmuAddService - Adds the service name to the Restart Manager session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddService( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzServiceName + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pSession->cs); + + hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0); + ExitOnFailure(hr, "Failed to add the service name to the array."); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuRegisterResources - Registers resources for the Restart Manager. + +This should be called rarely because it is expensive to run. Call +functions like RmuAddFile for multiple resources then commit them +as a batch of updates to RmuRegisterResources. + +Duplicate resources appear to be handled by Restart Manager. +Only one WM_QUERYENDSESSION is being sent for each top-level window. + +********************************************************************/ +extern "C" HRESULT DAPI RmuRegisterResources( + __in PRMU_SESSION pSession + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); + + ::EnterCriticalSection(&pSession->cs); + + er = vpfnRmRegisterResources( + pSession->dwSessionHandle, + pSession->cFilenames, + pSession->rgsczFilenames, + pSession->cApplications, + pSession->rgApplications, + pSession->cServiceNames, + pSession->rgsczServiceNames + ); + ExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); + + // Empty the arrays if registered in case additional resources are added later. + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuEndSession - Ends the session. + +If the session was joined by RmuJoinSession, any remaining resources +are registered before the session is ended (left). + +********************************************************************/ +extern "C" HRESULT DAPI RmuEndSession( + __in PRMU_SESSION pSession + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); + + // Make sure all resources are registered if we joined the session. + if (!pSession->fStartedSessionHandle) + { + hr = RmuRegisterResources(pSession); + ExitOnFailure(hr, "Failed to register remaining resources."); + } + + er = vpfnRmEndSession(pSession->dwSessionHandle); + ExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); + +LExit: + if (pSession->fInitialized) + { + ::DeleteCriticalSection(&pSession->cs); + } + + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); + ReleaseNullMem(pSession); + + RmuUninitialize(); + + return hr; +} + +static HRESULT RmuInitialize() +{ + HRESULT hr = S_OK; + HMODULE hModule = NULL; + + LONG iRef = ::InterlockedIncrement(&vcRmuInitialized); + if (1 == iRef && !vhModule) + { + hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule); + ExitOnFailure(hr, "Failed to load the rstrtmgr.dll module."); + + vpfnRmJoinSession = reinterpret_cast(::GetProcAddress(hModule, "RmJoinSession")); + ExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll."); + + vpfnRmRegisterResources = reinterpret_cast(::GetProcAddress(hModule, "RmRegisterResources")); + ExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll."); + + vpfnRmEndSession = reinterpret_cast(::GetProcAddress(hModule, "RmEndSession")); + ExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll."); + + vhModule = hModule; + } + +LExit: + return hr; +} + +static void RmuUninitialize() +{ + LONG iRef = ::InterlockedDecrement(&vcRmuInitialized); + if (0 == iRef && vhModule) + { + vpfnRmJoinSession = NULL; + vpfnRmEndSession = NULL; + vpfnRmRegisterResources = NULL; + + ::FreeLibrary(vhModule); + vhModule = NULL; + } +} + +static HRESULT RmuApplicationArrayAlloc( + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, + __inout LPUINT pcApplications, + __in DWORD dwProcessId, + __in FILETIME ProcessStartTime + ) +{ + HRESULT hr = S_OK; + RM_UNIQUE_PROCESS *pApplication = NULL; + + hr = MemEnsureArraySize(reinterpret_cast(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE); + ExitOnFailure(hr, "Failed to allocate memory for the application array."); + + pApplication = static_cast(&(*prgApplications)[*pcApplications]); + pApplication->dwProcessId = dwProcessId; + pApplication->ProcessStartTime = ProcessStartTime; + + ++(*pcApplications); + +LExit: + return hr; +} + +static HRESULT RmuApplicationArrayFree( + __in RM_UNIQUE_PROCESS *rgApplications + ) +{ + HRESULT hr = S_OK; + + hr = MemFree(rgApplications); + ExitOnFailure(hr, "Failed to free memory for the application array."); + +LExit: + return hr; +} diff --git a/src/dutil/rssutil.cpp b/src/dutil/rssutil.cpp new file mode 100644 index 00000000..db49d954 --- /dev/null +++ b/src/dutil/rssutil.cpp @@ -0,0 +1,632 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT ParseRssDocument( + __in IXMLDOMDocument *pixd, + __out RSS_CHANNEL **ppChannel + ); +static HRESULT ParseRssChannel( + __in IXMLDOMNode *pixnChannel, + __out RSS_CHANNEL **ppChannel + ); +static HRESULT ParseRssItem( + __in IXMLDOMNode *pixnItem, + __in DWORD cItem, + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ); +static HRESULT ParseRssUnknownElement( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement + ); +static HRESULT ParseRssUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ); +static void FreeRssUnknownElementList( + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement + ); +static void FreeRssUnknownAttributeList( + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ); + + +/******************************************************************** + RssInitialize - Initialize RSS utilities. + +*********************************************************************/ +extern "C" HRESULT DAPI RssInitialize() +{ + return XmlInitialize(); +} + + +/******************************************************************** + RssUninitialize - Uninitialize RSS utilities. + +*********************************************************************/ +extern "C" void DAPI RssUninitialize() +{ + XmlUninitialize(); +} + + +/******************************************************************** + RssParseFromString - parses out an RSS channel from a string. + +*********************************************************************/ +extern "C" HRESULT DAPI RssParseFromString( + __in_z LPCWSTR wzRssString, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(wzRssString); + Assert(ppChannel); + + HRESULT hr = S_OK; + RSS_CHANNEL *pNewChannel = NULL; + IXMLDOMDocument *pixdRss = NULL; + + hr = XmlLoadDocument(wzRssString, &pixdRss); + ExitOnFailure(hr, "Failed to load RSS string as XML document."); + + hr = ParseRssDocument(pixdRss, &pNewChannel); + ExitOnFailure(hr, "Failed to parse RSS document."); + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseObject(pixdRss); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + RssParseFromFile - parses out an RSS channel from a file path. + +*********************************************************************/ +extern "C" HRESULT DAPI RssParseFromFile( + __in_z LPCWSTR wzRssFile, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(wzRssFile); + Assert(ppChannel); + + HRESULT hr = S_OK; + RSS_CHANNEL *pNewChannel = NULL; + IXMLDOMDocument *pixdRss = NULL; + + hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss); + ExitOnFailure(hr, "Failed to load RSS string as XML document."); + + hr = ParseRssDocument(pixdRss, &pNewChannel); + ExitOnFailure(hr, "Failed to parse RSS document."); + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseObject(pixdRss); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + RssFreeChannel - parses out an RSS channel from a string. + +*********************************************************************/ +extern "C" void DAPI RssFreeChannel( + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ) +{ + if (pChannel) + { + for (DWORD i = 0; i < pChannel->cItems; ++i) + { + ReleaseStr(pChannel->rgItems[i].wzTitle); + ReleaseStr(pChannel->rgItems[i].wzLink); + ReleaseStr(pChannel->rgItems[i].wzDescription); + ReleaseStr(pChannel->rgItems[i].wzGuid); + ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl); + ReleaseStr(pChannel->rgItems[i].wzEnclosureType); + + FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements); + } + + ReleaseStr(pChannel->wzTitle); + ReleaseStr(pChannel->wzLink); + ReleaseStr(pChannel->wzDescription); + FreeRssUnknownElementList(pChannel->pUnknownElements); + + MemFree(pChannel); + } +} + + +/******************************************************************** + ParseRssDocument - parses out an RSS channel from a loaded XML DOM document. + +*********************************************************************/ +static HRESULT ParseRssDocument( + __in IXMLDOMDocument *pixd, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(pixd); + Assert(ppChannel); + + HRESULT hr = S_OK; + IXMLDOMElement *pRssElement = NULL; + IXMLDOMNodeList *pChannelNodes = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + RSS_CHANNEL *pNewChannel = NULL; + + // + // Get the document element and start processing channels. + // + hr = pixd ->get_documentElement(&pRssElement); + ExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); + + hr = pRssElement->get_childNodes(&pChannelNodes); + ExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); + + while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"channel")) + { + hr = ParseRssChannel(pNode, &pNewChannel); + ExitOnFailure(hr, "Failed to parse RSS channel."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pChannelNodes); + ReleaseObject(pRssElement); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + ParseRssChannel - parses out an RSS channel from a loaded XML DOM element. + +*********************************************************************/ +static HRESULT ParseRssChannel( + __in IXMLDOMNode *pixnChannel, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(pixnChannel); + Assert(ppChannel); + + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + RSS_CHANNEL *pNewChannel = NULL; + long cItems = 0; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + + // + // First, calculate how many RSS items we're going to have and allocate + // the RSS_CHANNEL structure + // + hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList); + ExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); + + hr = pNodeList->get_length(&cItems); + ExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); + + pNewChannel = static_cast(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE)); + ExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); + + pNewChannel->cItems = cItems; + + // + // Process the elements under a channel now. + // + hr = pixnChannel->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); + + cItems = 0; // reset the counter and use this to walk through the channel items + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"title")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS channel title."); + + hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS channel title."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS channel link."); + + hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS channel link."); + } + else if (0 == lstrcmpW(bstrNodeName, L"description")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS channel description."); + + hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS channel description."); + } + else if (0 == lstrcmpW(bstrNodeName, L"ttl")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS channel description."); + + pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10); + } + else if (0 == lstrcmpW(bstrNodeName, L"item")) + { + hr = ParseRssItem(pNode, cItems, pNewChannel); + ExitOnFailure(hr, "Failed to parse RSS item."); + + ++cItems; + } + else + { + hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements); + ExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeValue); + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + ParseRssItem - parses out an RSS item from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseRssItem( + __in IXMLDOMNode *pixnItem, + __in DWORD cItem, + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ) +{ + HRESULT hr = S_OK; + + RSS_ITEM *pItem = NULL; + IXMLDOMNodeList *pNodeList = NULL; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + + // + // First make sure we're dealing with a valid item. + // + if (pChannel->cItems <= cItem) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected number of items parsed."); + } + + pItem = pChannel->rgItems + cItem; + + // + // Process the elements under an item now. + // + hr = pixnItem->get_childNodes(&pNodeList); + ExitOnFailure(hr, "Failed to get child nodes of RSS item element."); + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"title")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS channel title."); + + hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS item title."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS channel link."); + + hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS item link."); + } + else if (0 == lstrcmpW(bstrNodeName, L"description")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS item description."); + + hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS item description."); + } + else if (0 == lstrcmpW(bstrNodeName, L"guid")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS item guid."); + + hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS item guid."); + } + else if (0 == lstrcmpW(bstrNodeName, L"pubDate")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS item guid."); + + hr = TimeFromString(bstrNodeValue, &pItem->ftPublished); + ExitOnFailure(hr, "Failed to convert RSS item time."); + } + else if (0 == lstrcmpW(bstrNodeName, L"enclosure")) + { + hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS item enclosure url."); + + hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); + ReleaseNullBSTR(bstrNodeValue); + + hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize); + ExitOnFailure(hr, "Failed to get RSS item enclosure length."); + + hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue); + ExitOnFailure(hr, "Failed to get RSS item enclosure type."); + + hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); + } + else + { + hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements); + ExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeValue); + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + +LExit: + ReleaseBSTR(bstrNodeValue); + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseRssUnknownElement( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement + ) +{ + Assert(ppUnknownElement); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + RSS_UNKNOWN_ELEMENT* pNewUnknownElement; + + pNewUnknownElement = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE)); + ExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); + ExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get unknown element namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + ExitOnFailure(hr, "Failed to get unknown element name."); + + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); + ExitOnFailure(hr, "Failed to allocate RSS unknown element name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get unknown element value."); + + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS unknown element value."); + + hr = pNode->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "Failed get attributes on RSS unknown element."); + + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) + { + hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); + ExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); + + ReleaseNullObject(pixnAttribute); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); + + RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownElement; + pNewUnknownElement = NULL; + +LExit: + FreeRssUnknownElementList(pNewUnknownElement); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + + return hr; +} + + +/******************************************************************** + ParseRssUnknownAttribute - parses out attribute from an unknown element + +*********************************************************************/ +static HRESULT ParseRssUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ) +{ + Assert(ppUnknownAttribute); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; + + pNewUnknownAttribute = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE)); + ExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); + ExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get unknown attribute namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + ExitOnFailure(hr, "Failed to get unknown attribute name."); + + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); + ExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + ExitOnFailure(hr, "Failed to get unknown attribute value."); + + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); + ExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); + + RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownAttribute; + pNewUnknownAttribute = NULL; + +LExit: + FreeRssUnknownAttributeList(pNewUnknownAttribute); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + + return hr; +} + + +/******************************************************************** + FreeRssUnknownElement - releases all of the memory used by a list of unknown elements + +*********************************************************************/ +static void FreeRssUnknownElementList( + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement + ) +{ + while (pUnknownElement) + { + RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement; + pUnknownElement = pUnknownElement->pNext; + + FreeRssUnknownAttributeList(pFree->pAttributes); + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzElement); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} + + +/******************************************************************** + FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes + +*********************************************************************/ +static void FreeRssUnknownAttributeList( + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ) +{ + while (pUnknownAttribute) + { + RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; + pUnknownAttribute = pUnknownAttribute->pNext; + + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzAttribute); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} diff --git a/src/dutil/sceutil.cpp b/src/dutil/sceutil.cpp new file mode 100644 index 00000000..cdb1623b --- /dev/null +++ b/src/dutil/sceutil.cpp @@ -0,0 +1,2489 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include "sceutil.h" + +// Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB. +#define MAX_SQLCE_DATABASE_SIZE 4091 + +// In case of some older versions of sqlce_oledb.h don't have these definitions, define some types. +#ifndef DBTYPEFOR_DBLENGTH +#ifdef _WIN64 +#define SKIP_SCE_COMPILE +#else +#define SCE_32BIT_ONLY +typedef DWORD DBLENGTH; +typedef LONG DBROWOFFSET; +typedef LONG DBROWCOUNT; +typedef DWORD DBCOUNTITEM; +typedef DWORD DBORDINAL; +typedef LONG DB_LORDINAL; +typedef DWORD DBBKMARK; +typedef DWORD DBBYTEOFFSET; +typedef DWORD DBREFCOUNT; +typedef DWORD DB_UPARAMS; +typedef LONG DB_LPARAMS; +typedef DWORD DBHASHVALUE; +typedef DWORD DB_DWRESERVE; +typedef LONG DB_LRESERVE; +typedef DWORD DB_URESERVE; +#endif + +#endif + +#ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit + +// structs +struct SCE_DATABASE_INTERNAL +{ + // In case we call DllGetClassObject on a specific file + HMODULE hSqlCeDll; + + volatile LONG dwTransactionRefcount; + IDBInitialize *pIDBInitialize; + IDBCreateSession *pIDBCreateSession; + ITransactionLocal *pITransactionLocal; + IDBProperties *pIDBProperties; + IOpenRowset *pIOpenRowset; + ISessionProperties *pISessionProperties; + + BOOL fChanges; // This database has changed + BOOL fPendingChanges; // Some changes are pending, upon transaction commit + BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back + BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail + + // If the database was opened as read-only, we copied it here - so delete it on close + LPWSTR sczTempDbFile; +}; + +struct SCE_ROW +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal; + + SCE_TABLE_SCHEMA *pTableSchema; + IRowset *pIRowset; + HROW hRow; + BOOL fInserting; + + DWORD dwBindingIndex; + DBBINDING *rgBinding; + SIZE_T cbOffset; + BYTE *pbData; +}; + +struct SCE_QUERY +{ + SCE_TABLE_SCHEMA *pTableSchema; + SCE_INDEX_SCHEMA *pIndexSchema; + SCE_DATABASE_INTERNAL *pDatabaseInternal; + + // Accessor build-up members + DWORD dwBindingIndex; + DBBINDING *rgBinding; + SIZE_T cbOffset; + BYTE *pbData; +}; + +struct SCE_QUERY_RESULTS +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal; + IRowset *pIRowset; + SCE_TABLE_SCHEMA *pTableSchema; +}; + +extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW); +extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY); +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS); + +// The following is the internal Sce-maintained table to tell the identifier and version of the schema +const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] = +{ + { + L"AppIdentifier", + DBTYPE_WSTR, + 0, + FALSE, + TRUE, + FALSE, + NULL, + 0, + 0 + }, + { + L"Version", + DBTYPE_I4, + 0, + FALSE, + FALSE, + FALSE, + NULL, + 0, + 0 + } +}; + +const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] = +{ + L"SceSchemaTablev1", + _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA), + (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA, + 0, + NULL, + NULL, + NULL +}; + +// internal function declarations + +// Creates an instance of SQL Server CE object, returning IDBInitialize object +// If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject +// on that file specifically. Otherwise it calls CoCreateInstance +static HRESULT CreateSqlCe( + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out IDBInitialize **ppIDBInitialize, + __out_opt HMODULE *phSqlCeDll + ); +static HRESULT RunQuery( + __in BOOL fRange, + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, + __out SCE_QUERY_RESULTS **ppsqrhHandle + ); +static HRESULT FillOutColumnDescFromSchema( + __in const SCE_COLUMN_SCHEMA *pSchema, + __out DBCOLUMNDESC pColumnDesc + ); +static HRESULT EnsureSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ); +static HRESULT OpenSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ); +static HRESULT SetColumnValue( + __in const SCE_TABLE_SCHEMA *pTableSchema, + __in DWORD dwColumnIndex, + __in_bcount_opt(cbSize) const BYTE *pbData, + __in SIZE_T cbSize, + __inout DBBINDING *pBinding, + __inout SIZE_T *pcbOffset, + __inout BYTE **ppbBuffer + ); +static HRESULT GetColumnValue( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbData, + __out SIZE_T *pcbSize + ); +static HRESULT GetColumnValueFixed( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __in DWORD cbSize, + __out BYTE *pbData + ); +static HRESULT EnsureLocalColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema + ); +static HRESULT EnsureForeignColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ); +static HRESULT SetSessionProperties( + __in ISessionProperties *pISessionProperties + ); +static HRESULT GetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __out LPWSTR *psczSchemaType, + __out DWORD *pdwVersion + ); +static HRESULT SetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __in LPCWSTR wzSchemaType, + __in DWORD dwVersion + ); +static void ReleaseDatabase( + SCE_DATABASE *pDatabase + ); +static void ReleaseDatabaseInternal( + SCE_DATABASE_INTERNAL *pDatabaseInternal + ); + +// function definitions +extern "C" HRESULT DAPI SceCreateDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out SCE_DATABASE **ppDatabase + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDirectory = NULL; + SCE_DATABASE *pNewSceDatabase = NULL; + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; + IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL; + DBPROPSET rgdbpDataSourcePropSet[2] = { }; + DBPROP rgdbpDataSourceProp[2] = { }; + DBPROP rgdbpDataSourceSsceProp[1] = { }; + + pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); + + pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); + + pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); + + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast(&pIDBDataSourceAdmin)); + ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface"); + + hr = PathGetDirectory(sczFile, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile); + + hr = DirEnsureExists(sczDirectory, NULL); + ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory); + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile); + + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[1].vValue.vt = VT_I4; + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; + + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); + + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE; + + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); + + hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL); + ExitOnFailure(hr, "Failed to create data source"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); + ExitOnFailure(hr, "Failed to get IDBProperties interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); + ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); + + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); + ExitOnFailure(hr, "Failed to get ISessionProperties interface"); + + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); + ExitOnFailure(hr, "Failed to set session properties"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); + ExitOnFailure(hr, "Failed to get IOpenRowset interface"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); + ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); + + *ppDatabase = pNewSceDatabase; + pNewSceDatabase = NULL; + +LExit: + ReleaseStr(sczDirectory); + ReleaseObject(pIDBDataSourceAdmin); + ReleaseDatabase(pNewSceDatabase); + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); + + return hr; +} + +extern "C" HRESULT DAPI SceOpenDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzExpectedSchemaType, + __in DWORD dwExpectedVersion, + __out SCE_DATABASE **ppDatabase, + __in BOOL fReadOnly + ) +{ + HRESULT hr = S_OK; + DWORD dwVersionFound = 0; + WCHAR wzTempDbFile[MAX_PATH]; + LPCWSTR wzPathToOpen = NULL; + LPWSTR sczSchemaType = NULL; + SCE_DATABASE *pNewSceDatabase = NULL; + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; + DBPROPSET rgdbpDataSourcePropSet[2] = { }; + DBPROP rgdbpDataSourceProp[2] = { }; + DBPROP rgdbpDataSourceSsceProp[1] = { }; + + pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); + + pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); + + pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); + + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); + ExitOnFailure(hr, "Failed to get IDBProperties interface"); + + // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now. + if (fReadOnly) + { + hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile)); + ExitOnFailure(hr, "Failed to get temp path"); + + hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE); + ExitOnFailure(hr, "Failed to copy file to temp path"); + + hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0); + ExitOnFailure(hr, "Failed to copy temp db file path"); + + wzPathToOpen = (LPCWSTR)wzTempDbFile; + } + else + { + wzPathToOpen = sczFile; + } + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen); + + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[1].vValue.vt = VT_I4; + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; + + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); + + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE; + + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); + + hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet); + ExitOnFailure(hr, "Failed to set initial properties to open database"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize(); + ExitOnFailure(hr, "Failed to open database: %ls", sczFile); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); + ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); + + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); + ExitOnFailure(hr, "Failed to get ISessionProperties interface"); + + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); + ExitOnFailure(hr, "Failed to set session properties"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); + ExitOnFailure(hr, "Failed to get IOpenRowset interface"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); + ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); + + hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound); + ExitOnFailure(hr, "Failed to find schema version of database"); + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1)) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE); + ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType); + } + else if (dwVersionFound != dwExpectedVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); + ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound); + } + + *ppDatabase = pNewSceDatabase; + pNewSceDatabase = NULL; + +LExit: + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); + ReleaseStr(sczSchemaType); + ReleaseDatabase(pNewSceDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceEnsureDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __in SCE_DATABASE_SCHEMA *pdsSchema, + __out SCE_DATABASE **ppDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE *pDatabase = NULL; + + if (FileExistsEx(sczFile, NULL)) + { + hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE); + ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile); + } + else + { + hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase); + ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile); + + hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion); + ExitOnFailure(hr, "Failed to set schema version of database"); + } + + hr = EnsureSchema(pDatabase, pdsSchema); + ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile); + + // Keep a pointer to the schema in the SCE_DATABASE object for future reference + pDatabase->pdsSchema = pdsSchema; + + *ppDatabase = pDatabase; + pDatabase = NULL; + +LExit: + ReleaseDatabase(pDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceIsTableEmpty( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out BOOL *pfEmpty + ) +{ + HRESULT hr = S_OK; + SCE_ROW_HANDLE row = NULL; + + hr = SceGetFirstRow(pDatabase, dwTableIndex, &row); + if (E_NOTFOUND == hr) + { + *pfEmpty = TRUE; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to get first row while checking if table is empty"); + + *pfEmpty = FALSE; + +LExit: + ReleaseSceRow(row); + + return hr; +} + +extern "C" HRESULT DAPI SceGetFirstRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + DBCOUNTITEM cRowsObtained = 0; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + SCE_ROW *pRow = NULL; + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + + hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER); + ExitOnFailure(hr, "Failed to reset IRowset position to beginning"); + + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = hRow; + pRow->pTableSchema = pTable; + pRow->pIRowset = pTable->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + +LExit: + return hr; +} + +HRESULT DAPI SceGetNextRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + DBCOUNTITEM cRowsObtained = 0; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + SCE_ROW *pRow = NULL; + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = hRow; + pRow->pTableSchema = pTable; + pRow->pIRowset = pTable->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceBeginTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + if (pDatabaseInternal->fTransactionBadState) + { + // We're in a hosed transaction state - we can't start a new one + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE)); + } + + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); + + if (1 == pDatabaseInternal->dwTransactionRefcount) + { + hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); + ExitOnFailure(hr, "Failed to start transaction"); + } + +LExit: + if (FAILED(hr)) + { + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + } + + return hr; +} + +extern "C" HRESULT DAPI SceCommitTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + Assert(0 < pDatabaseInternal->dwTransactionRefcount); + + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + + if (0 == pDatabaseInternal->dwTransactionRefcount) + { + if (pDatabaseInternal->fRollbackTransaction) + { + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); + ExitOnFailure(hr, "Failed to abort transaction"); + } + else + { + hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0); + ExitOnFailure(hr, "Failed to commit transaction"); + } + + if (pDatabaseInternal->fPendingChanges) + { + pDatabaseInternal->fPendingChanges = FALSE; + pDatabaseInternal->fChanges = TRUE; + } + + pDatabaseInternal->fRollbackTransaction = FALSE; + } + +LExit: + // If we tried to commit and failed, the caller should subsequently call rollback + if (FAILED(hr)) + { + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); + } + + return hr; +} + +extern "C" HRESULT DAPI SceRollbackTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + Assert(0 < pDatabaseInternal->dwTransactionRefcount); + + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + + if (0 == pDatabaseInternal->dwTransactionRefcount) + { + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); + ExitOnFailure(hr, "Failed to abort transaction"); + pDatabaseInternal->fPendingChanges = FALSE; + + pDatabaseInternal->fRollbackTransaction = FALSE; + } + else + { + pDatabaseInternal->fRollbackTransaction = TRUE; + } + +LExit: + // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?) + // but mark the database as bad so the user gets an error if they try to start a new transaction. + if (FAILED(hr)) + { + TraceError(hr, "Failed to rollback transaction"); + pDatabaseInternal->fTransactionBadState = TRUE; + } + + return hr; +} + +extern "C" HRESULT DAPI SceDeleteRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(*pRowHandle); + IRowsetChange *pIRowsetChange = NULL; + DBROWSTATUS rowStatus = DBROWSTATUS_S_OK; + + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange interface"); + + hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus); + ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus); + + ReleaseNullSceRow(*pRowHandle); + +LExit: + ReleaseObject(pIRowsetChange); + + return hr; +} + +extern "C" HRESULT DAPI ScePrepareInsert( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = NULL; + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = DB_NULL_HROW; + pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + pRow->pIRowset = pRow->pTableSchema->pIRowset; + pRow->pIRowset->AddRef(); + pRow->fInserting = TRUE; + + *pRowHandle = reinterpret_cast(pRow); + pRow = NULL; + +LExit: + ReleaseSceRow(pRow); + + return hr; +} + +extern "C" HRESULT DAPI SceFinishUpdate( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + IAccessor *pIAccessor = NULL; + IRowsetChange *pIRowsetChange = NULL; + DBBINDSTATUS *rgBindStatus = NULL; + HACCESSOR hAccessor = DB_NULL_HACCESSOR; + HROW hRow = DB_NULL_HROW; + + hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + +// This can be used when stepping through the debugger to see bind failures +#ifdef DEBUG + if (0 < pRow->dwBindingIndex) + { + hr = MemEnsureArraySize(reinterpret_cast(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0); + ExitOnFailure(hr, "Failed to ensure binding status array size"); + } +#endif + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange interface"); + + if (pRow->fInserting) + { + hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow); + ExitOnFailure(hr, "Failed to insert new row"); + + pRow->hRow = hRow; + ReleaseNullObject(pRow->pIRowset); + pRow->pIRowset = pRow->pTableSchema->pIRowset; + pRow->pIRowset->AddRef(); + } + else + { + hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData); + ExitOnFailure(hr, "Failed to update existing row"); + } + + if (0 < pRow->pDatabaseInternal->dwTransactionRefcount) + { + pRow->pDatabaseInternal->fPendingChanges = TRUE; + } + else + { + pRow->pDatabaseInternal->fChanges = TRUE; + } + +LExit: + if (DB_NULL_HACCESSOR != hAccessor) + { + pIAccessor->ReleaseAccessor(hAccessor, NULL); + } + ReleaseMem(rgBindStatus); + ReleaseObject(pIAccessor); + ReleaseObject(pIRowsetChange); + + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD dwValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as qword"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const BOOL fValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + short int sValue = fValue ? 0xFFFF : 0x0000; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR)); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnNull( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as empty value"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + DBTIMESTAMP dbTimeStamp = { }; + + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + dbTimeStamp.year = pst->wYear; + dbTimeStamp.month = pst->wMonth; + dbTimeStamp.day = pst->wDay; + dbTimeStamp.hour = pst->wHour; + dbTimeStamp.minute = pst->wMinute; + dbTimeStamp.second = pst->wSecond; + // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second, + // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds + // are involved (even when rounded the way SQL CE returns them). + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbBuffer, + __inout SIZE_T *pcbBuffer + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get binary data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD *pdwValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast(pdwValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get dword data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __in DWORD64 *pqwValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast(pqwValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get qword data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out BOOL *pfValue + ) +{ + HRESULT hr = S_OK; + short int sValue = 0; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast(&sValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get data out of column"); + + if (sValue == 0x0000) + { + *pfValue = FALSE; + } + else + { + *pfValue = TRUE; + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_z LPWSTR *psczValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + SIZE_T cbSize = 0; + + hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast(psczValue), &cbSize); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get string data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + DBTIMESTAMP dbTimeStamp = { }; + + hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast(&dbTimeStamp)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get string data out of column"); + + pst->wYear = dbTimeStamp.year; + pst->wMonth = dbTimeStamp.month; + pst->wDay = dbTimeStamp.day; + pst->wHour = dbTimeStamp.hour; + pst->wMinute = dbTimeStamp.minute; + pst->wSecond = dbTimeStamp.second; + +LExit: + return hr; +} + +extern "C" void DAPI SceCloseTable( + __in SCE_TABLE_SCHEMA *pTable + ) +{ + ReleaseNullObject(pTable->pIRowsetChange); + ReleaseNullObject(pTable->pIRowset); +} + +extern "C" BOOL DAPI SceDatabaseChanged( + __in SCE_DATABASE *pDatabase + ) +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + return pDatabaseInternal->fChanges; +} + +void DAPI SceResetDatabaseChanged( + __in SCE_DATABASE *pDatabase + ) +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + pDatabaseInternal->fChanges = FALSE; +} + +extern "C" HRESULT DAPI SceCloseDatabase( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + + ReleaseDatabase(pDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceBeginQuery( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __in DWORD dwIndex, + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_QUERY *psq = static_cast(MemAlloc(sizeof(SCE_QUERY), TRUE)); + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query"); + + psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]); + psq->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns); + + psq->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query"); + + *psqhHandle = static_cast(psq); + psq = NULL; + +LExit: + ReleaseSceQuery(psq); + + return hr; +} + +HRESULT DAPI SceSetQueryColumnBinary( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnDword( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD dwValue + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as dword"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnQword( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as qword"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnBool( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const BOOL fValue + ) +{ + HRESULT hr = S_OK; + short int sValue = fValue ? 1 : 0; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as boolean"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnString( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_z_opt LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR)); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as string"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnSystemTime( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + DBTIMESTAMP dbTimeStamp = { }; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + dbTimeStamp.year = pst->wYear; + dbTimeStamp.month = pst->wMonth; + dbTimeStamp.day = pst->wDay; + dbTimeStamp.hour = pst->wHour; + dbTimeStamp.minute = pst->wMinute; + dbTimeStamp.second = pst->wSecond; + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnEmpty( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as empty value"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceRunQueryExact( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY_RESULTS *pQueryResults = NULL; + + hr = RunQuery(FALSE, *psqhHandle, &pQueryResults); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to run query exact"); + + hr = SceGetNextResultRow(reinterpret_cast(pQueryResults), pRowHandle); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get next row out of results"); + +LExit: + ReleaseNullSceQuery(*psqhHandle); + ReleaseSceQueryResults(pQueryResults); + + return hr; +} + +extern "C" HRESULT DAPI SceRunQueryRange( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast(psqrhHandle); + + hr = RunQuery(TRUE, *psqhHandle, ppQueryResults); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to run query for range"); + +LExit: + ReleaseNullSceQuery(*psqhHandle); + + return hr; +} + +extern "C" HRESULT DAPI SceGetNextResultRow( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + DBCOUNTITEM cRowsObtained = 0; + SCE_ROW *pRow = NULL; + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); + + Assert(pRowHandle && (*pRowHandle == NULL)); + + hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pQueryResults->pDatabaseInternal); + pRow->hRow = hRow; + pRow->pTableSchema = pQueryResults->pTableSchema; + pRow->pIRowset = pQueryResults->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + pRow = NULL; + hRow = DB_NULL_HROW; + +LExit: + if (DB_NULL_HROW != hRow) + { + pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); + } + ReleaseSceRow(pRow); + + return hr; +} + +extern "C" void DAPI SceFreeRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ) +{ + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + if (DB_NULL_HROW != pRow->hRow) + { + pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL); + } + ReleaseObject(pRow->pIRowset); + ReleaseMem(pRow->rgBinding); + ReleaseMem(pRow->pbData); + ReleaseMem(pRow); +} + +void DAPI SceFreeQuery( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle + ) +{ + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + ReleaseMem(pQuery->rgBinding); + ReleaseMem(pQuery->pbData); + ReleaseMem(pQuery); +} + +void DAPI SceFreeQueryResults( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle + ) +{ + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); + + ReleaseObject(pQueryResults->pIRowset); + ReleaseMem(pQueryResults); +} + +// internal function definitions +static HRESULT CreateSqlCe( + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out IDBInitialize **ppIDBInitialize, + __out_opt HMODULE *phSqlCeDll + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + LPWSTR sczDirectory = NULL; + LPWSTR sczDllFullPath = NULL; + + if (NULL == wzSqlCeDllPath) + { + hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast(ppIDBInitialize)); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + } + else + { + // First try loading DLL from the path of our EXE + hr = PathForCurrentProcess(&sczPath, NULL); + ExitOnFailure(hr, "Failed to get path for current process"); + + hr = PathGetDirectory(sczPath, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory of current process"); + + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); + + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); + + // If that failed, fallback to loading from current path + if (NULL == *phSqlCeDll) + { + hr = DirGetCurrent(&sczDirectory); + ExitOnFailure(hr, "Failed to get current directory"); + + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); + + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); + ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath); + } + + HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**); + pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject"); + + IClassFactory* pFactory = NULL; + hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory); + ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath); + ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL"); + + hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize); + pFactory->Release(); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczDirectory); + ReleaseStr(sczDllFullPath); + + return hr; +} + +static HRESULT RunQuery( + __in BOOL fRange, + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults + ) +{ + HRESULT hr = S_OK; + DBID tableID = { }; + DBID indexID = { }; + IAccessor *pIAccessor = NULL; + IRowsetIndex *pIRowsetIndex = NULL; + IRowset *pIRowset = NULL; + HACCESSOR hAccessor = DB_NULL_HACCESSOR; + SCE_QUERY *pQuery = reinterpret_cast(psqhHandle); + SCE_QUERY_RESULTS *pQueryResults = NULL; + DBPROPSET rgdbpIndexPropSet[1]; + DBPROP rgdbpIndexProp[1]; + + rgdbpIndexPropSet[0].cProperties = 1; + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; + + rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex; + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpIndexProp[0].colid = DB_NULLID; + rgdbpIndexProp[0].vValue.vt = VT_BOOL; + rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE; + + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pQuery->pTableSchema->wzName); + + indexID.eKind = DBKIND_NAME; + indexID.uName.pwszName = const_cast(pQuery->pIndexSchema->wzName); + + hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex); + ExitOnFailure(hr, "Failed to open IRowsetIndex"); + + hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast(&pIRowset)); + ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex"); + + hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL); + ExitOnFailure(hr, "Failed to create accessor"); + + if (!fRange) + { + hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ); + if (DB_E_NOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to seek to record"); + } + else + { + // If ALL columns in the index were specified, do a full key match + if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns) + { + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH); + } + else + { + // Otherwise, just match the specified keys. + // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it + // So instead, we set the start and end to the same partial key, and then allow inclusive matching + // This appears to accomplish the same thing + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0); + } + if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to set range to find records"); + } + + pQueryResults = reinterpret_cast(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE)); + ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct"); + + pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal; + pQueryResults->pTableSchema = pQuery->pTableSchema; + pQueryResults->pIRowset = pIRowset; + pIRowset = NULL; + + *ppQueryResults = pQueryResults; + pQueryResults = NULL; + +LExit: + if (DB_NULL_HACCESSOR != hAccessor) + { + pIAccessor->ReleaseAccessor(hAccessor, NULL); + } + ReleaseObject(pIAccessor); + ReleaseObject(pIRowset); + ReleaseObject(pIRowsetIndex); + ReleaseMem(pQueryResults); + ReleaseSceQueryResults(pQueryResults); + + return hr; +} + +static HRESULT FillOutColumnDescFromSchema( + __in const SCE_COLUMN_SCHEMA *pColumnSchema, + __out DBCOLUMNDESC *pColumnDesc + ) +{ + HRESULT hr = S_OK; + DWORD dwColumnProperties = 0; + DWORD dwColumnPropertyIndex = 0; + BOOL fFixedSize = FALSE; + + pColumnDesc->dbcid.eKind = DBKIND_NAME; + pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName; + pColumnDesc->wType = pColumnSchema->dbtColumnType; + pColumnDesc->ulColumnSize = pColumnSchema->dwLength; + if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType)) + { + fFixedSize = FALSE; + } + else + { + fFixedSize = TRUE; + } + + dwColumnProperties = 1; + if (pColumnSchema->fAutoIncrement) + { + ++dwColumnProperties; + } + if (!pColumnSchema->fNullable) + { + ++dwColumnProperties; + } + + if (0 < dwColumnProperties) + { + pColumnDesc->cPropertySets = 1; + pColumnDesc->rgPropertySets = reinterpret_cast(MemAlloc(sizeof(DBPROPSET), TRUE)); + ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters"); + + pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties; + pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN; + pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE)); + + dwColumnPropertyIndex = 0; + if (pColumnSchema->fAutoIncrement) + { + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE; + ++dwColumnPropertyIndex; + } + if (!pColumnSchema->fNullable) + { + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE; + ++dwColumnPropertyIndex; + } + + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE; + ++dwColumnPropertyIndex; + } + +LExit: + return hr; +} + +static HRESULT EnsureSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + BOOL fInTransaction = FALSE; + BOOL fSchemaNeedsSetup = TRUE; + DBID tableID = { }; + DBID indexID = { }; + DBPROPSET rgdbpIndexPropSet[1]; + DBPROPSET rgdbpRowSetPropSet[1]; + DBPROP rgdbpIndexProp[1]; + DBPROP rgdbpRowSetProp[1]; + DBCOLUMNDESC *rgColumnDescriptions = NULL; + DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL; + DWORD cIndexColumnDescriptions = 0; + DWORD dwTableColumnIndex = 0; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + ITableDefinition *pTableDefinition = NULL; + IIndexDefinition *pIndexDefinition = NULL; + IRowsetIndex *pIRowsetIndex = NULL; + + rgdbpRowSetPropSet[0].cProperties = 1; + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; + + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpRowSetProp[0].colid = DB_NULLID; + rgdbpRowSetProp[0].vValue.vt = VT_BOOL; + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; + + rgdbpIndexPropSet[0].cProperties = 1; + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX; + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; + + rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS; + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpIndexProp[0].colid = DB_NULLID; + rgdbpIndexProp[0].vValue.vt = VT_I4; + rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL; + + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast(&pTableDefinition)); + ExitOnFailure(hr, "Failed to get ITableDefinition for table creation"); + + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast(&pIndexDefinition)); + ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation"); + + hr = SceBeginTransaction(pDatabase); + ExitOnFailure(hr, "Failed to start transaction for ensuring schema"); + fInTransaction = TRUE; + + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist + rgColumnDescriptions = static_cast(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE)); + ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table"); + + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i); + ExitOnFailure(hr, "Failed to fill out column description from schema"); + } + + // First try to open the table - or if it doesn't exist, create it + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); + if (DB_E_NOTABLE == hr) + { + // The table doesn't exist, so let's create it + hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL); + ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + else + { + ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName); + + // Close any rowset we opened + ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset); + + // If it does exist, make sure all columns are in the table + // Only nullable columns can be added to an existing table + for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable) + hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL); + if (DB_E_DUPLICATECOLUMNID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName); + } + } + +#pragma prefast(push) +#pragma prefast(disable:26010) + hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable); +#pragma prefast(pop) + ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); + + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + if (NULL != rgColumnDescriptions[i].rgPropertySets) + { + ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties); + ReleaseMem(rgColumnDescriptions[i].rgPropertySets); + } + } + + ReleaseNullMem(rgColumnDescriptions); + if (0 < pdsSchema->rgTables[dwTable].cIndexes) + { + // Now create indexes for the table + for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex) + { + indexID.eKind = DBKIND_NAME; + indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName; + + // Check if the index exists + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex); + if (SUCCEEDED(hr)) + { + // TODO: If one with the same name exists, check if the schema actually matches + ReleaseNullObject(pIRowsetIndex); + continue; + } + hr = S_OK; + + hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns); + + rgIndexColumnDescriptions = reinterpret_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions"); + cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns; + + for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex) + { + dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex]; + + rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast(MemAlloc(sizeof(DBID), TRUE)); + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME; + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName); + rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC; + } + + hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL); + if (DB_E_DUPLICATEINDEXID == hr) + { + // If the index already exists, no worries + hr = S_OK; + } + ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName); + + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) + { + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); + } + + cIndexColumnDescriptions = 0; + ReleaseNullMem(rgIndexColumnDescriptions); + } + } + } + + // Now once all tables have been created, create foreign key relationships + if (fSchemaNeedsSetup) + { + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // Setup any constraints for the table's columns + hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema); + ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + } + + hr = SceCommitTransaction(pDatabase); + ExitOnFailure(hr, "Failed to commit transaction for ensuring schema"); + fInTransaction = FALSE; + + hr = OpenSchema(pDatabase, pdsSchema); + ExitOnFailure(hr, "Failed to open schema"); + +LExit: + ReleaseObject(pTableDefinition); + ReleaseObject(pIndexDefinition); + ReleaseObject(pIRowsetIndex); + + if (fInTransaction) + { + SceRollbackTransaction(pDatabase); + } + + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) + { + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); + } + + ReleaseMem(rgIndexColumnDescriptions); + ReleaseMem(rgColumnDescriptions); + + return hr; +} + +static HRESULT OpenSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + DBID tableID = { }; + DBPROPSET rgdbpRowSetPropSet[1]; + DBPROP rgdbpRowSetProp[1]; + + rgdbpRowSetPropSet[0].cProperties = 1; + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; + + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpRowSetProp[0].colid = DB_NULLID; + rgdbpRowSetProp[0].vValue.vt = VT_BOOL; + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; + + // Finally, open all tables + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // And finally, open the table's standard interfaces + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); + ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName); + + hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + +LExit: + return hr; +} + +static HRESULT SetColumnValue( + __in const SCE_TABLE_SCHEMA *pTableSchema, + __in DWORD dwColumnIndex, + __in_bcount_opt(cbSize) const BYTE *pbData, + __in SIZE_T cbSize, + __inout DBBINDING *pBinding, + __inout SIZE_T *pcbOffset, + __inout BYTE **ppbBuffer + ) +{ + HRESULT hr = S_OK; + size_t cbNewOffset = *pcbOffset; + + pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column + pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; + pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; + + pBinding->obLength = cbNewOffset; + + hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset); + ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value"); + + pBinding->obValue = cbNewOffset; + + hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset); + ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize); + + pBinding->obStatus = cbNewOffset; + pBinding->eParamIO = DBPARAMIO_INPUT; + + hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset); + ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value"); + + pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType; + pBinding->cbMaxLen = static_cast(cbSize); + + if (NULL == *ppbBuffer) + { + *ppbBuffer = reinterpret_cast(MemAlloc(cbNewOffset, TRUE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string"); + } + else + { + *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string"); + } + + *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = static_cast(cbSize); + *pcbOffset += sizeof(DBBYTEOFFSET); + memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize); + *pcbOffset += cbSize; + if (NULL == pbData) + { + *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL; + } + *pcbOffset += sizeof(DBSTATUS); + +LExit: + return hr; +} + +static HRESULT GetColumnValue( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbData, + __out SIZE_T *pcbSize + ) +{ + HRESULT hr = S_OK; + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; + IAccessor *pIAccessor = NULL; + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; + DWORD dwDataSize = 0; + void *pvRawData = NULL; + DBBINDING dbBinding = { }; + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; + + dbBinding.iOrdinal = dwColumnIndex + 1; + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; + dbBinding.dwPart = DBPART_LENGTH; + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; + + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); + ExitOnFailure(hr, "Failed to get size of data"); + + // For variable-length columns, zero data returned means NULL + if (0 == dwDataSize) + { + ExitFunction1(hr = E_NOTFOUND); + } + + if (NULL != ppbData) + { + dbBinding.dwPart = DBPART_VALUE; + dbBinding.cbMaxLen = dwDataSize; + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); + } + + if (DBTYPE_WSTR == dbBinding.wType) + { + hr = StrAlloc(reinterpret_cast(&pvRawData), dwDataSize / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex); + } + else + { + pvRawData = MemAlloc(dwDataSize, TRUE); + ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData); + ExitOnFailure(hr, "Failed to read data value"); + + ReleaseMem(*ppbData); + *ppbData = reinterpret_cast(pvRawData); + pvRawData = NULL; + } + + *pcbSize = dwDataSize; + +LExit: + ReleaseMem(pvRawData); + + if (DB_NULL_HACCESSOR != hAccessorLength) + { + pIAccessor->ReleaseAccessor(hAccessorLength, NULL); + } + if (DB_NULL_HACCESSOR != hAccessorValue) + { + pIAccessor->ReleaseAccessor(hAccessorValue, NULL); + } + ReleaseObject(pIAccessor); + + return hr; +} + +static HRESULT GetColumnValueFixed( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __in DWORD cbSize, + __out BYTE *pbData + ) +{ + HRESULT hr = S_OK; + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; + IAccessor *pIAccessor = NULL; + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; + DWORD dwDataSize = 0; + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; + DBBINDING dbBinding = { }; + + dbBinding.iOrdinal = dwColumnIndex + 1; + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; + dbBinding.dwPart = DBPART_LENGTH; + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; + + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value"); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); + ExitOnFailure(hr, "Failed to get size of data"); + + if (0 == dwDataSize) + { + ExitFunction1(hr = E_NOTFOUND); + } + + dbBinding.dwPart = DBPART_VALUE; + dbBinding.cbMaxLen = cbSize; + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast(pbData)); + ExitOnFailure(hr, "Failed to read data value"); + +LExit: + if (DB_NULL_HACCESSOR != hAccessorLength) + { + pIAccessor->ReleaseAccessor(hAccessorLength, NULL); + } + if (DB_NULL_HACCESSOR != hAccessorValue) + { + pIAccessor->ReleaseAccessor(hAccessorValue, NULL); + } + ReleaseObject(pIAccessor); + + return hr; +} + +static HRESULT EnsureLocalColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema + ) +{ + HRESULT hr = S_OK; + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; + DBCONSTRAINTDESC dbcdConstraint = { }; + DBID dbConstraintID = { }; + DBID dbLocalColumnID = { }; + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; + + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); + + for (DWORD i = 0; i < pTableSchema->cColumns; ++i) + { + pCurrentColumn = pTableSchema->rgColumns + i; + + // Add a primary key constraint for this column, if one exists + if (pCurrentColumn->fPrimaryKey) + { + // Setup DBID for new constraint + dbConstraintID.eKind = DBKIND_NAME; + dbConstraintID.uName.pwszName = const_cast(L"PrimaryKey"); + dbcdConstraint.pConstraintID = &dbConstraintID; + + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY; + + dbLocalColumnID.eKind = DBKIND_NAME; + dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); + dbcdConstraint.cColumns = 1; + dbcdConstraint.rgColumnList = &dbLocalColumnID; + + dbcdConstraint.pReferencedTableID = NULL; + dbcdConstraint.cForeignKeyColumns = 0; + dbcdConstraint.rgForeignKeyColumnList = NULL; + dbcdConstraint.pwszConstraintText = NULL; + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; + dbcdConstraint.MatchType = DBMATCHTYPE_NONE; + dbcdConstraint.Deferrability = 0; + dbcdConstraint.cReserved = 0; + dbcdConstraint.rgReserved = NULL; + + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); + if (DB_E_DUPLICATECONSTRAINTID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName); + } + } + +LExit: + ReleaseObject(pTableDefinitionWithConstraints); + + return hr; +} + +static HRESULT EnsureForeignColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ) +{ + HRESULT hr = S_OK; + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; + DBCONSTRAINTDESC dbcdConstraint = { }; + DBID dbConstraintID = { }; + DBID dbLocalColumnID = { }; + DBID dbForeignTableID = { }; + DBID dbForeignColumnID = { }; + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; + + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); + + for (DWORD i = 0; i < pTableSchema->cColumns; ++i) + { + pCurrentColumn = pTableSchema->rgColumns + i; + + // Add a foreign key constraint for this column, if one exists + if (NULL != pCurrentColumn->wzRelationName) + { + // Setup DBID for new constraint + dbConstraintID.eKind = DBKIND_NAME; + dbConstraintID.uName.pwszName = const_cast(pCurrentColumn->wzRelationName); + dbcdConstraint.pConstraintID = &dbConstraintID; + + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY; + + dbForeignColumnID.eKind = DBKIND_NAME; + dbForeignColumnID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName); + dbcdConstraint.cColumns = 1; + dbcdConstraint.rgColumnList = &dbForeignColumnID; + + dbForeignTableID.eKind = DBKIND_NAME; + dbForeignTableID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName); + dbcdConstraint.pReferencedTableID = &dbForeignTableID; + + dbLocalColumnID.eKind = DBKIND_NAME; + dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); + dbcdConstraint.cForeignKeyColumns = 1; + dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID; + + dbcdConstraint.pwszConstraintText = NULL; + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; + dbcdConstraint.MatchType = DBMATCHTYPE_FULL; + dbcdConstraint.Deferrability = 0; + dbcdConstraint.cReserved = 0; + dbcdConstraint.rgReserved = NULL; + + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); + if (DB_E_DUPLICATECONSTRAINTID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName); + } + } + +LExit: + ReleaseObject(pTableDefinitionWithConstraints); + + return hr; +} + +static HRESULT SetSessionProperties( + __in ISessionProperties *pISessionProperties + ) +{ + HRESULT hr = S_OK; + DBPROP rgdbpDataSourceProp[1]; + DBPROPSET rgdbpDataSourcePropSet[1]; + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH; + + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = 1; + + hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet); + ExitOnFailure(hr, "Failed to set session properties"); + +LExit: + return hr; +} + +static HRESULT GetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __out LPWSTR *psczSchemaType, + __out DWORD *pdwVersion + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSchemaType = NULL; + DWORD dwVersionFound = 0; + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; + // Database object with our alternate schema + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; + SCE_ROW_HANDLE sceRow = NULL; + + hr = OpenSchema(pDatabase, &fullSchema); + ExitOnFailure(hr, "Failed to ensure internal version schema"); + + hr = SceGetFirstRow(&database, 0, &sceRow); + ExitOnFailure(hr, "Failed to get first row in internal version schema table"); + + hr = SceGetColumnString(sceRow, 0, &sczSchemaType); + ExitOnFailure(hr, "Failed to get internal schematype"); + + hr = SceGetColumnDword(sceRow, 1, &dwVersionFound); + ExitOnFailure(hr, "Failed to get internal version"); + + *psczSchemaType = sczSchemaType; + sczSchemaType = NULL; + *pdwVersion = dwVersionFound; + +LExit: + SceCloseTable(&schemaTable); // ignore failure + ReleaseStr(sczSchemaType); + ReleaseSceRow(sceRow); + + return hr; +} + +static HRESULT SetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __in LPCWSTR wzSchemaType, + __in DWORD dwVersion + ) +{ + HRESULT hr = S_OK; + BOOL fInSceTransaction = FALSE; + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; + // Database object with our alternate schema + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; + SCE_ROW_HANDLE sceRow = NULL; + + hr = EnsureSchema(pDatabase, &fullSchema); + ExitOnFailure(hr, "Failed to ensure internal version schema"); + + hr = SceBeginTransaction(&database); + ExitOnFailure(hr, "Failed to begin transaction"); + fInSceTransaction = TRUE; + + hr = SceGetFirstRow(&database, 0, &sceRow); + if (E_NOTFOUND == hr) + { + hr = ScePrepareInsert(&database, 0, &sceRow); + ExitOnFailure(hr, "Failed to insert only row into internal version schema table"); + } + else + { + ExitOnFailure(hr, "Failed to get first row in internal version schema table"); + } + + hr = SceSetColumnString(sceRow, 0, wzSchemaType); + ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType); + + hr = SceSetColumnDword(sceRow, 1, dwVersion); + ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion); + + hr = SceFinishUpdate(sceRow); + ExitOnFailure(hr, "Failed to insert first row in internal version schema table"); + + hr = SceCommitTransaction(&database); + ExitOnFailure(hr, "Failed to commit transaction"); + fInSceTransaction = FALSE; + +LExit: + SceCloseTable(&schemaTable); // ignore failure + ReleaseSceRow(sceRow); + if (fInSceTransaction) + { + SceRollbackTransaction(&database); + } + + return hr; +} + +static void ReleaseDatabase( + SCE_DATABASE *pDatabase + ) +{ + if (NULL != pDatabase) + { + if (NULL != pDatabase->pdsSchema) + { + for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i) + { + SceCloseTable(pDatabase->pdsSchema->rgTables + i); + } + } + + if (NULL != pDatabase->sdbHandle) + { + ReleaseDatabaseInternal(reinterpret_cast(pDatabase->sdbHandle)); + } + } + ReleaseMem(pDatabase); +} + +static void ReleaseDatabaseInternal( + SCE_DATABASE_INTERNAL *pDatabaseInternal + ) +{ + HRESULT hr = S_OK; + + if (NULL != pDatabaseInternal) + { + ReleaseObject(pDatabaseInternal->pITransactionLocal); + ReleaseObject(pDatabaseInternal->pIOpenRowset); + ReleaseObject(pDatabaseInternal->pISessionProperties); + ReleaseObject(pDatabaseInternal->pIDBCreateSession); + ReleaseObject(pDatabaseInternal->pIDBProperties); + + if (NULL != pDatabaseInternal->pIDBInitialize) + { + hr = pDatabaseInternal->pIDBInitialize->Uninitialize(); + if (FAILED(hr)) + { + TraceError(hr, "Failed to call uninitialize on IDBInitialize"); + } + ReleaseObject(pDatabaseInternal->pIDBInitialize); + } + + if (NULL != pDatabaseInternal->hSqlCeDll) + { + if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to free sql ce dll"); + } + } + } + + // If there was a temp file we copied to (for read-only databases), delete it after close + if (NULL != pDatabaseInternal->sczTempDbFile) + { + hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile); + if (FAILED(hr)) + { + TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile); + } + ReleaseStr(pDatabaseInternal->sczTempDbFile); + } + + ReleaseMem(pDatabaseInternal); +} + +#endif // end SKIP_SCE_COMPILE diff --git a/src/dutil/shelutil.cpp b/src/dutil/shelutil.cpp new file mode 100644 index 00000000..a69c9eaa --- /dev/null +++ b/src/dutil/shelutil.cpp @@ -0,0 +1,327 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ); +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ); + +/******************************************************************** + ShelFunctionOverride - overrides the shell functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI ShelFunctionOverride( + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW + ) +{ + vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW; +} + + +/******************************************************************** + ShelExec() - executes a target. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExec( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd, + __in_opt HWND hwndParent, + __out_opt HANDLE* phProcess + ) +{ + HRESULT hr = S_OK; + SHELLEXECUTEINFOW shExecInfo = {}; + + shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; + shExecInfo.hwnd = hwndParent; + shExecInfo.lpVerb = wzVerb; + shExecInfo.lpFile = wzTargetPath; + shExecInfo.lpParameters = wzParameters; + shExecInfo.lpDirectory = wzWorkingDirectory; + shExecInfo.nShow = nShowCmd; + + if (!vpfnShellExecuteExW(&shExecInfo)) + { + ExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); + } + + if (phProcess) + { + *phProcess = shExecInfo.hProcess; + shExecInfo.hProcess = NULL; + } + +LExit: + ReleaseHandle(shExecInfo.hProcess); + + return hr; +} + + +/******************************************************************** + ShelExecUnelevated() - executes a target unelevated. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExecUnelevated( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd + ) +{ + HRESULT hr = S_OK; + BSTR bstrTargetPath = NULL; + VARIANT vtParameters = { }; + VARIANT vtVerb = { }; + VARIANT vtWorkingDirectory = { }; + VARIANT vtShow = { }; + IShellView* psv = NULL; + IShellDispatch2* psd = NULL; + + bstrTargetPath = ::SysAllocString(wzTargetPath); + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); + + if (wzParameters && *wzParameters) + { + vtParameters.vt = VT_BSTR; + vtParameters.bstrVal = ::SysAllocString(wzParameters); + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); + } + + if (wzVerb && *wzVerb) + { + vtVerb.vt = VT_BSTR; + vtVerb.bstrVal = ::SysAllocString(wzVerb); + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); + } + + if (wzWorkingDirectory && *wzWorkingDirectory) + { + vtWorkingDirectory.vt = VT_BSTR; + vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); + } + + vtShow.vt = VT_INT; + vtShow.intVal = nShowCmd; + + hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); + ExitOnFailure(hr, "Failed to get desktop shell view."); + + hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); + ExitOnFailure(hr, "Failed to get shell dispatch from view."); + + hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); + } + ExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); + +LExit: + ReleaseObject(psd); + ReleaseObject(psv); + ReleaseBSTR(vtWorkingDirectory.bstrVal); + ReleaseBSTR(vtVerb.bstrVal); + ReleaseBSTR(vtParameters.bstrVal); + ReleaseBSTR(bstrTargetPath); + + return hr; +} + + +/******************************************************************** + ShelGetFolder() - gets a folder by CSIDL. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ) +{ + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH]; + + hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); + ExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); + + hr = StrAllocString(psczFolderPath, wzPath, 0); + ExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + return hr; +} + + +/******************************************************************** + ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. + + Note: return E_NOTIMPL if called on pre-Vista operating systems. +*******************************************************************/ +#ifndef REFKNOWNFOLDERID +#define REFKNOWNFOLDERID REFGUID +#endif + +#ifndef KF_FLAG_CREATE +#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition +#endif + +EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( + REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath + ); + +extern "C" HRESULT DAPI ShelGetKnownFolder( + __out_z LPWSTR* psczFolderPath, + __in REFKNOWNFOLDERID rfidFolder + ) +{ + HRESULT hr = S_OK; + HMODULE hShell32Dll = NULL; + PFN_SHGetKnownFolderPath pfn = NULL; + LPWSTR pwzPath = NULL; + + hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll); + if (E_MODNOTFOUND == hr) + { + TraceError(hr, "Failed to load shell32.dll"); + ExitFunction1(hr = E_NOTIMPL); + } + ExitOnFailure(hr, "Failed to load shell32.dll."); + + pfn = reinterpret_cast(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); + ExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); + + hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); + ExitOnFailure(hr, "Failed to get known folder path."); + + hr = StrAllocString(psczFolderPath, pwzPath, 0); + ExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + if (pwzPath) + { + ::CoTaskMemFree(pwzPath); + } + + if (hShell32Dll) + { + ::FreeLibrary(hShell32Dll); + } + + return hr; +} + + +// Internal functions. + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IShellWindows* psw = NULL; + HWND hwnd = NULL; + IDispatch* pdisp = NULL; + VARIANT vEmpty = {}; // VT_EMPTY + IShellBrowser* psb = NULL; + IShellFolder* psf = NULL; + IShellView* psv = NULL; + + // use the shell view for the desktop using the shell windows automation to find the + // desktop web browser and then grabs its view + // returns IShellView, IFolderView and related interfaces + hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); + ExitOnFailure(hr, "Failed to get shell view."); + + hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); + if (S_OK == hr) + { + hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); + ExitOnFailure(hr, "Failed to get desktop window."); + + hr = psb->QueryActiveShellView(&psv); + ExitOnFailure(hr, "Failed to get active shell view."); + + hr = psv->QueryInterface(riid, ppv); + ExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else if (S_FALSE == hr) + { + //Windows XP + hr = SHGetDesktopFolder(&psf); + ExitOnFailure(hr, "Failed to get desktop folder."); + + hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); + ExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else + { + ExitOnFailure(hr, "Failed to get desktop window."); + } + +LExit: + ReleaseObject(psv); + ReleaseObject(psb); + ReleaseObject(psf); + ReleaseObject(pdisp); + ReleaseObject(psw); + + return hr; +} + +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IDispatch *pdispBackground = NULL; + IShellFolderViewDual *psfvd = NULL; + IDispatch *pdisp = NULL; + + // From a shell view object, gets its automation interface and from that get the shell + // application object that implements IShellDispatch2 and related interfaces. + hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); + ExitOnFailure(hr, "Failed to get the automation interface for shell."); + + hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); + ExitOnFailure(hr, "Failed to get shell folder view dual."); + + hr = psfvd->get_Application(&pdisp); + ExitOnFailure(hr, "Failed to application object."); + + hr = pdisp->QueryInterface(riid, ppv); + ExitOnFailure(hr, "Failed to get IShellDispatch2."); + +LExit: + ReleaseObject(pdisp); + ReleaseObject(psfvd); + ReleaseObject(pdispBackground); + + return hr; +} diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp new file mode 100644 index 00000000..099c6ae9 --- /dev/null +++ b/src/dutil/sqlutil.cpp @@ -0,0 +1,868 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// okay, this may look a little weird, but sqlutil.h cannot be in the +// pre-compiled header because we need to #define these things so the +// correct GUID's get pulled into this object file +#include +#define DBINITCONSTANTS +#include "sqlutil.h" + +// private prototypes +static HRESULT FileSpecToString( + __in const SQL_FILESPEC* psf, + __out LPWSTR* ppwz + ); + +static HRESULT EscapeSqlIdentifier( + __in_z LPCWSTR wzDatabase, + __deref_out_z LPWSTR* ppwz + ); + + +/******************************************************************** + SqlConnectDatabase - establishes a connection to a database + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlConnectDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out IDBCreateSession** ppidbSession + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession); + + HRESULT hr = S_OK; + IDBInitialize* pidbInitialize = NULL; + IDBProperties* pidbProperties = NULL; + + LPWSTR pwzServerInstance = NULL; + DBPROP rgdbpInit[4]; + DBPROPSET rgdbpsetInit[1]; + ULONG cProperties = 0; + + memset(rgdbpInit, 0, sizeof(rgdbpInit)); + memset(rgdbpsetInit, 0, sizeof(rgdbpsetInit)); + + //obtain access to the SQLOLEDB provider + hr = ::CoCreateInstance(CLSID_SQLOLEDB, NULL, CLSCTX_INPROC_SERVER, + IID_IDBInitialize, (LPVOID*)&pidbInitialize); + ExitOnFailure(hr, "failed to create IID_IDBInitialize object"); + + // if there is an instance + if (wzInstance && *wzInstance) + { + hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance); + } + else + { + hr = StrAllocString(&pwzServerInstance, wzServer, 0); + } + ExitOnFailure(hr, "failed to allocate memory for the server instance"); + + // server[\instance] + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance); + ++cProperties; + + // database + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase); + ++cProperties; + + if (fIntegratedAuth) + { + // username + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication + ++cProperties; + } + else + { + // username + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser); + ++cProperties; + + // password + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword); + ++cProperties; + } + + // put the properties into a set + rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpsetInit[0].rgProperties = rgdbpInit; + rgdbpsetInit[0].cProperties = cProperties; + + // create and set the property set + hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); + ExitOnFailure(hr, "failed to get IID_IDBProperties object"); + hr = pidbProperties->SetProperties(1, rgdbpsetInit); + ExitOnFailure(hr, "failed to set properties"); + + //initialize connection to datasource + hr = pidbInitialize->Initialize(); + ExitOnFailure(hr, "failed to initialize connection to database: %ls", wzDatabase); + + hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); + +LExit: + for (; 0 < cProperties; cProperties--) + { + ::VariantClear(&rgdbpInit[cProperties - 1].vValue); + } + + ReleaseObject(pidbProperties); + ReleaseObject(pidbInitialize); + ReleaseStr(pwzServerInstance); + + return hr; +} + + +/******************************************************************** + SqlStartTransaction - Starts a new transaction that must be ended + +*********************************************************************/ +extern "C" HRESULT DAPI SqlStartTransaction( + __in IDBCreateSession* pidbSession, + __out IDBCreateCommand** ppidbCommand, + __out ITransaction** ppit + ) +{ + Assert(pidbSession && ppit); + + HRESULT hr = S_OK; + + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand); + ExitOnFailure(hr, "unable to create command from session"); + + hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit); + ExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal"); + + hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); + +LExit: + + return hr; +} + +/******************************************************************** + SqlEndTransaction - Ends the transaction + + NOTE: if fCommit, will commit the transaction, otherwise rolls back +*********************************************************************/ +extern "C" HRESULT DAPI SqlEndTransaction( + __in ITransaction* pit, + __in BOOL fCommit + ) +{ + Assert(pit); + + HRESULT hr = S_OK; + + if (fCommit) + { + hr = pit->Commit(FALSE, XACTTC_SYNC, 0); + ExitOnFailure(hr, "commit of transaction failed"); + } + else + { + hr = pit->Abort(NULL, FALSE, FALSE); + ExitOnFailure(hr, "abort of transaction failed"); + } + +LExit: + + return hr; +} + + +/******************************************************************** + SqlDatabaseExists - determines if database exists + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored + returns S_OK if database exist + returns S_FALSE if database does not exist + returns E_* on error +********************************************************************/ +extern "C" HRESULT DAPI SqlDatabaseExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDatabaseExists - determines if database exists + + NOTE: pidbSession must be connected to master database + returns S_OK if database exist + returns S_FALSE if database does not exist + returns E_* on error +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDatabaseExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + + LPWSTR pwzQuery = NULL; + IRowset* pirs = NULL; + + DBCOUNTITEM cRows = 0; + HROW rghRows[1]; + HROW* prow = rghRows; + + // + // query to see if the database exists + // + hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase); + ExitOnFailure(hr, "failed to allocate query string to ensure database exists"); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription); + ExitOnFailure(hr, "failed to get database list from 'master' database"); + Assert(pirs); + + // + // check to see if the database was returned + // + hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow); + ExitOnFailure(hr, "failed to get row with database name"); + + // succeeded but no database + if ((DB_S_ENDOFROWSET == hr) || (0 == cRows)) + { + hr = S_FALSE; + } + +LExit: + ReleaseObject(pirs); + ReleaseStr(pwzQuery); + + return hr; +} + + +/******************************************************************** + SqlDatabaseEnsureExists - creates a database if it does not exist + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlDatabaseEnsureExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to create the new database + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + ExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + + Assert(S_OK == hr); +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDatabaseEnsureExists - creates a database if it does not exist + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + + if (S_FALSE == hr) + { + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + ExitOnFailure(hr, "failed to create database: %1", wzDatabase); + } + // else database already exists, return S_FALSE + + Assert(S_OK == hr); +LExit: + + return hr; +} + + +/******************************************************************** + SqlCreateDatabase - creates a database on the server + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlCreateDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to create the new database + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + ExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + + Assert(S_OK == hr); +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionCreateDatabase - creates a database on the server + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionCreateDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzDbFile = NULL; + LPWSTR pwzLogFile = NULL; + LPWSTR pwzQuery = NULL; + LPWSTR pwzDatabaseEscaped = NULL; + + if (psfDatabase) + { + hr = FileSpecToString(psfDatabase, &pwzDbFile); + ExitOnFailure(hr, "failed to convert db filespec to string"); + } + + if (psfLog) + { + hr = FileSpecToString(psfLog, &pwzLogFile); + ExitOnFailure(hr, "failed to convert log filespec to string"); + } + + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); + ExitOnFailure(hr, "failed to escape database string"); + + 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""); + ExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); + ExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery); + +LExit: + ReleaseStr(pwzQuery); + ReleaseStr(pwzLogFile); + ReleaseStr(pwzDbFile); + ReleaseStr(pwzDatabaseEscaped); + + return hr; +} + + +/******************************************************************** + SqlDropDatabase - removes a database from a server if it exists + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlDropDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to search for wzDatabase + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + ExitOnFailure(hr, "Failed to connect to 'master' database"); + + hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription); + +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDropDatabase - removes a database from a server if it exists + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDropDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + LPWSTR pwzQuery = NULL; + LPWSTR pwzDatabaseEscaped = NULL; + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); + ExitOnFailure(hr, "failed to escape database string"); + + if (S_OK == hr) + { + hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped); + ExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); + ExitOnFailure(hr, "Failed to drop database"); + } + +LExit: + ReleaseStr(pwzQuery); + ReleaseStr(pwzDatabaseEscaped); + + return hr; +} + + +/******************************************************************** + SqlSessionExecuteQuery - executes a query and returns the results if desired + + NOTE: ppirs and pcRoes and pbstrErrorDescription are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionExecuteQuery( + __in IDBCreateSession* pidbSession, + __in __sql_command LPCWSTR wzSql, + __out_opt IRowset** ppirs, + __out_opt DBROWCOUNT* pcRows, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession); + + HRESULT hr = S_OK; + IDBCreateCommand* pidbCommand = NULL; + ICommandText* picmdText = NULL; + ICommand* picmd = NULL; + DBROWCOUNT cRows = 0; + + if (pcRows) + { + *pcRows = NULL; + } + + // + // create the command + // + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand); + ExitOnFailure(hr, "failed to create database session"); + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); + ExitOnFailure(hr, "failed to create command to execute session"); + + // + // set the sql text into the command + // + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); + ExitOnFailure(hr, "failed to get command text object for command"); + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); + ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + + // + // execute the command + // + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); + ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + + if (DB_S_ERRORSOCCURRED == hr) + { + hr = E_FAIL; + } + + if (pcRows) + { + *pcRows = cRows; + } + +LExit: + + if (FAILED(hr) && picmd && pbstrErrorDescription) + { + HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English + if (FAILED(hrGetErrors)) + { + ReleaseBSTR(*pbstrErrorDescription); + } + } + + ReleaseObject(picmd); + ReleaseObject(picmdText); + ReleaseObject(pidbCommand); + + return hr; +} + + +/******************************************************************** + SqlCommandExecuteQuery - executes a SQL command and returns the results if desired + + NOTE: ppirs and pcRoes are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlCommandExecuteQuery( + __in IDBCreateCommand* pidbCommand, + __in __sql_command LPCWSTR wzSql, + __out IRowset** ppirs, + __out DBROWCOUNT* pcRows + ) +{ + Assert(pidbCommand); + + HRESULT hr = S_OK; + ICommandText* picmdText = NULL; + ICommand* picmd = NULL; + DBROWCOUNT cRows = 0; + + if (pcRows) + { + *pcRows = NULL; + } + + // + // create the command + // + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); + ExitOnFailure(hr, "failed to create command to execute session"); + + // + // set the sql text into the command + // + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); + ExitOnFailure(hr, "failed to get command text object for command"); + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); + ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + + // + // execute the command + // + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); + ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + + if (DB_S_ERRORSOCCURRED == hr) + { + hr = E_FAIL; + } + + if (pcRows) + { + *pcRows = cRows; + } + +LExit: + ReleaseObject(picmd); + ReleaseObject(picmdText); + + return hr; +} + + +/******************************************************************** + SqlGetErrorInfo - gets error information from the last SQL function call + + NOTE: pbstrErrorSource and pbstrErrorDescription are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlGetErrorInfo( + __in IUnknown* pObjectWithError, + __in REFIID IID_InterfaceWithError, + __in DWORD dwLocaleId, + __out_opt BSTR* pbstrErrorSource, + __out_opt BSTR* pbstrErrorDescription + ) +{ + HRESULT hr = S_OK; + Assert(pObjectWithError); + + // interfaces needed to extract error information out + ISupportErrorInfo* pISupportErrorInfo = NULL; + IErrorInfo* pIErrorInfoAll = NULL; + IErrorRecords* pIErrorRecords = NULL; + IErrorInfo* pIErrorInfoRecord = NULL; + + // only ask for error information if the interface supports it. + hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo); + ExitOnFailure(hr, "No error information was found for object."); + + hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError); + ExitOnFailure(hr, "InterfaceWithError is not supported for object with error"); + + // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway + hr = ::GetErrorInfo(0, &pIErrorInfoAll); + ExitOnFailure(hr, "failed to get error info"); + + if (S_OK == hr && pIErrorInfoAll) + { + // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records + hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); + if (SUCCEEDED(hr)) + { + ULONG cErrors = 0; + pIErrorRecords->GetRecordCount(&cErrors); + + // get the error information for each record + for (ULONG i = 0; i < cErrors; ++i) + { + hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord); + if (SUCCEEDED(hr)) + { + if (pbstrErrorSource) + { + pIErrorInfoRecord->GetSource(pbstrErrorSource); + } + if (pbstrErrorDescription) + { + pIErrorInfoRecord->GetDescription(pbstrErrorDescription); + } + + ReleaseNullObject(pIErrorInfoRecord); + + break; // TODO: return more than one error in the future! + } + } + + ReleaseNullObject(pIErrorRecords); + } + else // we have a simple error record + { + if (pbstrErrorSource) + { + pIErrorInfoAll->GetSource(pbstrErrorSource); + } + if (pbstrErrorDescription) + { + pIErrorInfoAll->GetDescription(pbstrErrorDescription); + } + } + } + else + { + hr = E_NOMOREITEMS; + } + +LExit: + ReleaseObject(pIErrorInfoRecord); + ReleaseObject(pIErrorRecords); + ReleaseObject(pIErrorInfoAll); + ReleaseObject(pISupportErrorInfo); + + return hr; +} + + +// +// private +// + +/******************************************************************** + FileSpecToString + +*********************************************************************/ +static HRESULT FileSpecToString( + __in const SQL_FILESPEC* psf, + __out LPWSTR* ppwz + ) +{ + Assert(psf && ppwz); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + hr = StrAllocString(&pwz, L"(", 1024); + ExitOnFailure(hr, "failed to allocate string for database file info"); + + ExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info"); + ExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info"); + + hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName); + ExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName); + + hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename); + ExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename); + + if (0 != psf->wzSize[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize); + ExitOnFailure(hr, "failed to format database file info size: %s", psf->wzSize); + } + + if (0 != psf->wzMaxSize[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize); + ExitOnFailure(hr, "failed to format database file info maxsize: %s", psf->wzMaxSize); + } + + if (0 != psf->wzGrow[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow); + ExitOnFailure(hr, "failed to format database file info growth: %s", psf->wzGrow); + } + + hr = StrAllocFormatted(&pwz, L"%s)", pwz); + ExitOnFailure(hr, "failed to allocate string for file spec"); + + *ppwz = pwz; + pwz = NULL; // null here so it doesn't get freed below + +LExit: + ReleaseStr(pwz); + return hr; +} + +static HRESULT EscapeSqlIdentifier( + __in_z LPCWSTR wzIdentifier, + __deref_out_z LPWSTR* ppwz + ) +{ + Assert(ppwz); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + if (wzIdentifier == NULL) + { + //Just ignore a NULL identifier and clear out the result + ReleaseNullStr(*ppwz); + ExitFunction(); + } + + int cchIdentifier = lstrlenW(wzIdentifier); + + //If an empty string or already escaped just copy + if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']')) + { + hr = StrAllocString(&pwz, wzIdentifier, 0); + ExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier); + } + else + { + //escape it + hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier); + ExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier); + } + + *ppwz = pwz; + pwz = NULL; // null here so it doesn't get freed below + +LExit: + ReleaseStr(pwz); + return hr; +} diff --git a/src/dutil/srputil.cpp b/src/dutil/srputil.cpp new file mode 100644 index 00000000..9fc2f94a --- /dev/null +++ b/src/dutil/srputil.cpp @@ -0,0 +1,237 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +typedef BOOL (WINAPI *PFN_SETRESTOREPTW)( + __in PRESTOREPOINTINFOW pRestorePtSpec, + __out PSTATEMGRSTATUS pSMgrStatus + ); + +static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL; +static HMODULE vhSrClientDll = NULL; + + +static HRESULT InitializeComSecurity(); + + +DAPI_(HRESULT) SrpInitialize( + __in BOOL fInitializeComSecurity + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"srclient.dll", &vhSrClientDll); + if (FAILED(hr)) + { + ExitFunction1(hr = E_NOTIMPL); + } + + vpfnSRSetRestorePointW = reinterpret_cast(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW")); + ExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address."); + + // If allowed, initialize COM security to enable NetworkService, + // LocalService and System to make callbacks to the process + // calling System Restore. This is required for any process + // that calls SRSetRestorePoint. + if (fInitializeComSecurity) + { + hr = InitializeComSecurity(); + ExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore."); + } + +LExit: + if (FAILED(hr) && vhSrClientDll) + { + SrpUninitialize(); + } + + return hr; +} + +DAPI_(void) SrpUninitialize() +{ + if (vhSrClientDll) + { + ::FreeLibrary(vhSrClientDll); + vhSrClientDll = NULL; + vpfnSRSetRestorePointW = NULL; + } +} + +DAPI_(HRESULT) SrpCreateRestorePoint( + __in_z LPCWSTR wzApplicationName, + __in SRP_ACTION action + ) +{ + HRESULT hr = S_OK; + RESTOREPOINTINFOW restorePoint = { }; + STATEMGRSTATUS status = { }; + + if (!vpfnSRSetRestorePointW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + restorePoint.dwEventType = BEGIN_SYSTEM_CHANGE; + restorePoint.dwRestorePtType = (SRP_ACTION_INSTALL == action) ? APPLICATION_INSTALL : (SRP_ACTION_UNINSTALL == action) ? APPLICATION_UNINSTALL : MODIFY_SETTINGS; + ::StringCbCopyW(restorePoint.szDescription, sizeof(restorePoint.szDescription), wzApplicationName); + + if (!vpfnSRSetRestorePointW(&restorePoint, &status)) + { + ExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point."); + } + +LExit: + return hr; +} + + +// internal functions. + +static HRESULT InitializeComSecurity() +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + SECURITY_DESCRIPTOR sd = {0}; + EXPLICIT_ACCESS ea[5] = {0}; + ACL* pAcl = NULL; + ULONGLONG rgSidBA[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidLS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidNS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidPS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidSY[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + DWORD cbSid = 0; + + // Create the security descriptor explicitly as follows because + // CoInitializeSecurity() will not accept the relative security descriptors + // returned by ConvertStringSecurityDescriptorToSecurityDescriptor(). + // + // The result is a security descriptor that is equivalent to the following + // security descriptor definition language (SDDL) string: + // + // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)(A;;0x1;;;SY)(A;;0x1;;;BA) + // + + // Initialize the security descriptor. + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) + { + ExitWithLastError(hr, "Failed to initialize security descriptor for system restore."); + } + + // Create an administrator group security identifier (SID). + cbSid = sizeof(rgSidBA); + if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid)) + { + ExitWithLastError(hr, "Failed to create administrator SID for system restore."); + } + + // Create a local service security identifier (SID). + cbSid = sizeof(rgSidLS); + if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid)) + { + ExitWithLastError(hr, "Failed to create local service SID for system restore."); + } + + // Create a network service security identifier (SID). + cbSid = sizeof(rgSidNS); + if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid)) + { + ExitWithLastError(hr, "Failed to create network service SID for system restore."); + } + + // Create a personal account security identifier (SID). + cbSid = sizeof(rgSidPS); + if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid)) + { + ExitWithLastError(hr, "Failed to create self SID for system restore."); + } + + // Create a local service security identifier (SID). + cbSid = sizeof(rgSidSY); + if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid)) + { + ExitWithLastError(hr, "Failed to create local system SID for system restore."); + } + + // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and + // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required. + ea[0].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[0].grfAccessMode = SET_ACCESS; + ea[0].grfInheritance = NO_INHERITANCE; + ea[0].Trustee.pMultipleTrustee = NULL; + ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[0].Trustee.ptstrName = (LPTSTR)rgSidBA; + + ea[1].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[1].grfAccessMode = SET_ACCESS; + ea[1].grfInheritance = NO_INHERITANCE; + ea[1].Trustee.pMultipleTrustee = NULL; + ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[1].Trustee.ptstrName = (LPTSTR)rgSidLS; + + ea[2].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[2].grfAccessMode = SET_ACCESS; + ea[2].grfInheritance = NO_INHERITANCE; + ea[2].Trustee.pMultipleTrustee = NULL; + ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[2].Trustee.ptstrName = (LPTSTR)rgSidNS; + + ea[3].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[3].grfAccessMode = SET_ACCESS; + ea[3].grfInheritance = NO_INHERITANCE; + ea[3].Trustee.pMultipleTrustee = NULL; + ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[3].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[3].Trustee.ptstrName = (LPTSTR)rgSidPS; + + ea[4].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[4].grfAccessMode = SET_ACCESS; + ea[4].grfInheritance = NO_INHERITANCE; + ea[4].Trustee.pMultipleTrustee = NULL; + ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[4].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[4].Trustee.ptstrName = (LPTSTR)rgSidSY; + + // Create an access control list (ACL) using this ACE list. + er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl); + ExitOnWin32Error(er, hr, "Failed to create ACL for system restore."); + + // Set the security descriptor owner to Administrators. + if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE)) + { + ExitWithLastError(hr, "Failed to set administrators owner for system restore."); + } + + // Set the security descriptor group to Administrators. + if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE)) + { + ExitWithLastError(hr, "Failed to set administrators group access for system restore."); + } + + // Set the discretionary access control list (DACL) to the ACL. + if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) + { + ExitWithLastError(hr, "Failed to set DACL for system restore."); + } + + // Note that an explicit security descriptor is being passed in. + 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); + ExitOnFailure(hr, "Failed to initialize COM security for system restore."); + +LExit: + if (pAcl) + { + ::LocalFree(pAcl); + } + + return hr; +} diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp new file mode 100644 index 00000000..2e5e2f96 --- /dev/null +++ b/src/dutil/strutil.cpp @@ -0,0 +1,2730 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define ARRAY_GROWTH_SIZE 5 + +// Forward declarations. +static HRESULT AllocHelper( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocStringHelper( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocConcatHelper( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocFormattedArgsHelper( + __deref_out_z LPWSTR* ppwz, + __in BOOL fZeroOnRealloc, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +static HRESULT StrAllocStringMapInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in int cchSource, + __in DWORD dwMapFlags + ); + +/******************************************************************** +StrAlloc - allocates or reuses dynamic string memory + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAlloc( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ) +{ + return AllocHelper(ppwz, cch, FALSE); +} + +/******************************************************************** +StrAllocSecure - allocates or reuses dynamic string memory +If the memory needs to reallocated, calls SecureZeroMemory on the +original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocSecure( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ) +{ + return AllocHelper(ppwz, cch, TRUE); +} + +/******************************************************************** +AllocHelper - allocates or reuses dynamic string memory +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +static HRESULT AllocHelper( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && cch); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppwz) + { + if (fZeroOnRealloc) + { + LPVOID pvNew = NULL; + hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew); + ExitOnFailure(hr, "Failed to reallocate string"); + pwz = static_cast(pvNew); + } + else + { + pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE)); + } + } + else + { + pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); + } + + ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppwz = pwz; +LExit: + return hr; +} + + +/******************************************************************** +StrTrimCapacity - Frees any unnecessary memory associated with a string. + Purely used for optimization, generally only when a string + has been changing size, and will no longer grow. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrTrimCapacity( + __deref_out_z LPWSTR* ppwz + ) +{ + Assert(ppwz); + + HRESULT hr = S_OK; + DWORD_PTR cchLen = 0; + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + ExitOnFailure(hr, "Failed to calculate length of string"); + + ++cchLen; // Add 1 for null-terminator + + hr = StrAlloc(ppwz, cchLen); + ExitOnFailure(hr, "Failed to reallocate string"); + +LExit: + return hr; +} + + +/******************************************************************** +StrTrimWhitespace - allocates or reuses dynamic string memory and copies + in an existing string, excluding whitespace + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrTrimWhitespace( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource + ) +{ + HRESULT hr = S_OK; + int i = 0; + LPWSTR sczResult = NULL; + + // Ignore beginning whitespace + while (L' ' == *wzSource || L'\t' == *wzSource) + { + wzSource++; + } + + i = lstrlenW(wzSource); + // Overwrite ending whitespace with null characters + if (0 < i) + { + // start from the last non-null-terminator character in the array + for (i = i - 1; i > 0; --i) + { + if (L' ' != wzSource[i] && L'\t' != wzSource[i]) + { + break; + } + } + + ++i; + } + + hr = StrAllocString(&sczResult, wzSource, i); + ExitOnFailure(hr, "Failed to copy result string"); + + // Output result + *ppwz = sczResult; + sczResult = NULL; + +LExit: + ReleaseStr(sczResult); + + return hr; +} + + +/******************************************************************** +StrAnsiAlloc - allocates or reuses dynamic ANSI string memory + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAlloc( + __deref_out_ecount_part(cch, 0) LPSTR* ppsz, + __in DWORD_PTR cch + ) +{ + Assert(ppsz && cch); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppsz) + { + psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE)); + } + else + { + psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); + } + + ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppsz = psz; +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string. + Purely used for optimization, generally only when a string + has been changing size, and will no longer grow. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrAnsiTrimCapacity( + __deref_out_z LPSTR* ppz + ) +{ + Assert(ppz); + + HRESULT hr = S_OK; + DWORD_PTR cchLen = 0; + +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); +#pragma prefast(pop) + ExitOnFailure(hr, "Failed to calculate length of string"); + + ++cchLen; // Add 1 for null-terminator + + hr = StrAnsiAlloc(ppz, cchLen); + ExitOnFailure(hr, "Failed to reallocate string"); + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies + in an existing string, excluding whitespace + +NOTE: caller is responsible for freeing ppz even if function fails +********************************************************************/ +HRESULT DAPI StrAnsiTrimWhitespace( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR szSource + ) +{ + HRESULT hr = S_OK; + int i = 0; + LPSTR sczResult = NULL; + + // Ignore beginning whitespace + while (' ' == *szSource || '\t' == *szSource) + { + szSource++; + } + + i = lstrlen(szSource); + // Overwrite ending whitespace with null characters + if (0 < i) + { + // start from the last non-null-terminator character in the array + for (i = i - 1; i > 0; --i) + { + if (L' ' != szSource[i] && L'\t' != szSource[i]) + { + break; + } + } + + ++i; + } + + hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i); + ExitOnFailure(hr, "Failed to copy result string"); + + // Output result + *ppz = sczResult; + sczResult = NULL; + +LExit: + ReleaseStr(sczResult); + + return hr; +} + +/******************************************************************** +StrAllocString - allocates or reuses dynamic string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocString( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + return AllocStringHelper(ppwz, wzSource, cchSource, FALSE); +} + +/******************************************************************** +StrAllocStringSecure - allocates or reuses dynamic string memory and +copies in an existing string. If the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocStringSecure( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + return AllocStringHelper(ppwz, wzSource, cchSource, TRUE); +} + +/******************************************************************** +AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +static HRESULT AllocStringHelper( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && wzSource); // && *wzSource); + + HRESULT hr = S_OK; + DWORD_PTR cch = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchSource = lstrlenW(wzSource); + } + + DWORD_PTR cchNeeded; + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator + ExitOnFailure(hr, "source string is too long"); + + if (cch < cchNeeded) + { + cch = cchNeeded; + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + ExitOnFailure(hr, "failed to allocate string from string."); + } + + // copy everything (the NULL terminator will be included) + hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppsz even if function fails +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocString( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource, + __in UINT uiCodepage + ) +{ + Assert(ppsz && wzSource); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + DWORD_PTR cch = 0; + DWORD_PTR cchDest = cchSource; // at least enough + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL); + if (0 == cchDest) + { + ExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource); + } + + --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below + } + 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 + { + cchDest = cchSource - 1; + } + + if (cch < cchDest + 1) + { + cch = cchDest + 1; // add one for the NULL terminator + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppsz) + { + psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE)); + } + else + { + psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); + } + ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppsz = psz; + } + + if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL)) + { + ExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource); + } + (*ppsz)[cchDest] = L'\0'; + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCSTR szSource, + __in DWORD_PTR cchSource, + __in UINT uiCodepage + ) +{ + Assert(ppwz && szSource); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + DWORD_PTR cch = 0; + DWORD_PTR cchDest = cchSource; // at least enough + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0); + if (0 == cchDest) + { + ExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource); + } + + --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below + } + 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 + { + cchDest = cchSource - 1; + } + + if (cch < cchDest + 1) + { + cch = cchDest + 1; + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppwz) + { + pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE)); + } + else + { + pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); + } + + ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppwz = pwz; + } + + if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch)) + { + ExitWithLastError(hr, "failed to convert to unicode: %s", szSource); + } + (*ppwz)[cchDest] = L'\0'; + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppsz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +HRESULT DAPI StrAnsiAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCSTR szSource, + __in DWORD_PTR cchSource + ) +{ + Assert(ppsz && szSource); // && *szSource); + + HRESULT hr = S_OK; + DWORD_PTR cch = 0; + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchSource = lstrlenA(szSource); + } + + DWORD_PTR cchNeeded; + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator + ExitOnFailure(hr, "source string is too long"); + + if (cch < cchNeeded) + { + cch = cchNeeded; + hr = StrAnsiAlloc(ppsz, cch); + ExitOnFailure(hr, "failed to allocate string from string."); + } + + // copy everything (the NULL terminator will be included) +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); +#pragma prefast(pop) + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocPrefix - allocates or reuses dynamic string memory and + prefixes a string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchPrefix does not have to equal the length of wzPrefix +NOTE: if cchPrefix == 0, length of wzPrefix is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocPrefix( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzPrefix, + __in DWORD_PTR cchPrefix + ) +{ + Assert(ppwz && wzPrefix); + + HRESULT hr = S_OK; + DWORD_PTR cch = 0; + DWORD_PTR cchLen = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + ExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchPrefix) + { + hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast(&cchPrefix)); + ExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchPrefix + 1) + { + cch = cchPrefix + cchLen + 1; + hr = StrAlloc(ppwz, cch); + ExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix); + } + + if (*ppwz) + { + DWORD_PTR cb = cch * sizeof(WCHAR); + DWORD_PTR cbPrefix = cchPrefix * sizeof(WCHAR); + + memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix); + memcpy(*ppwz, wzPrefix, cbPrefix); + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocConcat( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE); +} + + +/******************************************************************** +StrAllocConcatSecure - allocates or reuses dynamic string memory and +adds an existing string. If the memory needs to reallocated, calls +SecureZeroMemory on the original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocConcatSecure( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE); +} + + +/******************************************************************** +AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +static HRESULT AllocConcatHelper( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && wzSource); // && *wzSource); + + HRESULT hr = S_OK; + DWORD_PTR cch = 0; + DWORD_PTR cchLen = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + ExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchSource) + { + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + ExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchSource + 1) + { + cch = (cchSource + cchLen + 1) * 2; + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + ExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource); + } + + if (*ppwz) + { + hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string + +NOTE: caller is responsible for freeing ppz even if function fails +NOTE: cchSource does not have to equal the length of pzSource +NOTE: if cchSource == 0, length of pzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocConcat( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR pzSource, + __in DWORD_PTR cchSource + ) +{ + Assert(ppz && pzSource); // && *pzSource); + + HRESULT hr = S_OK; + DWORD_PTR cch = 0; + DWORD_PTR cchLen = 0; + + if (*ppz) + { + cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); // convert the count in bytes to count in characters + +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); +#pragma prefast(pop) + ExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchSource) + { +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); +#pragma prefast(pop) + ExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchSource + 1) + { + cch = (cchSource + cchLen + 1) * 2; + hr = StrAnsiAlloc(ppz, cch); + ExitOnFailure(hr, "failed to allocate string from string: %ls", pzSource); + } + + if (*ppz) + { +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); +#pragma prefast(pop) + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocFormatted - allocates or reuses dynamic string memory and formats it + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(ppwz, wzFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAllocConcatFormatted - allocates or reuses dynamic string memory +and adds a formatted string + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocConcatFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args); + va_end(args); + ExitOnFailure(hr, "Failed to allocate formatted string"); + + hr = StrAllocConcat(ppwz, sczFormatted, 0); + +LExit: + ReleaseStr(sczFormatted); + + return hr; +} + + +/******************************************************************** +StrAllocFormattedSecure - allocates or reuses dynamic string memory +and formats it. If the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocFormatted( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + ... + ) +{ + Assert(ppsz && szFormat && *szFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAllocFormattedArgs - allocates or reuses dynamic string memory +and formats it with the passed in args + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFormattedArgs( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args); +} + + +/******************************************************************** +StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory +and formats it with the passed in args. + +If the memory needs to reallocated, calls SecureZeroMemory on the +original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFormattedArgsSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args); +} + + +/******************************************************************** +AllocFormattedArgsHelper - allocates or reuses dynamic string memory +and formats it with the passed in args. + +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +static HRESULT AllocFormattedArgsHelper( + __deref_out_z LPWSTR* ppwz, + __in BOOL fZeroOnRealloc, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + LPWSTR pwzOriginal = NULL; + SIZE_T cbOriginal = 0; + SIZE_T cchOriginal = 0; + + if (*ppwz) + { + cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cbOriginal) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + + cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters + cchOriginal = lstrlenW(*ppwz); + } + + if (0 == cch) // if there is no space in the string buffer + { + cch = 256; + + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + } + + // format the message (grow until it fits or there is a failure) + do + { + hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + if (!pwzOriginal) + { + // this allows you to pass the original string as a formatting argument and not crash + // save the original string and free it after the printf is complete + pwzOriginal = *ppwz; + *ppwz = NULL; + + // StringCchVPrintfW starts writing to the string... + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); + pwzOriginal[cchOriginal] = 0; + } + + cch *= 2; + + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + + hr = S_FALSE; + } + } while (S_FALSE == hr); + ExitOnFailure(hr, "failed to format string"); + +LExit: + if (pwzOriginal && fZeroOnRealloc) + { + SecureZeroMemory(pwzOriginal, cbOriginal); + } + + ReleaseStr(pwzOriginal); + + return hr; +} + + +/******************************************************************** +StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory +and formats it with the passed in args + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + __in va_list args + ) +{ + Assert(ppsz && szFormat && *szFormat); + + HRESULT hr = S_OK; + DWORD_PTR cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; + LPSTR pszOriginal = NULL; + DWORD cchOriginal = 0; + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + + cchOriginal = lstrlenA(*ppsz); + } + + if (0 == cch) // if there is no space in the string buffer + { + cch = 256; + hr = StrAnsiAlloc(ppsz, cch); + ExitOnFailure(hr, "failed to allocate string to format: %s", szFormat); + } + + // format the message (grow until it fits or there is a failure) + do + { +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args); +#pragma prefast(pop) + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + if (!pszOriginal) + { + // this allows you to pass the original string as a formatting argument and not crash + // save the original string and free it after the printf is complete + pszOriginal = *ppsz; + *ppsz = NULL; + // StringCchVPrintfW starts writing to the string... + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); + pszOriginal[cchOriginal] = 0; + } + cch *= 2; + hr = StrAnsiAlloc(ppsz, cch); + ExitOnFailure(hr, "failed to allocate string to format: %ls", szFormat); + hr = S_FALSE; + } + } while (S_FALSE == hr); + ExitOnFailure(hr, "failed to format string"); + +LExit: + ReleaseStr(pszOriginal); + + return hr; +} + + +/******************************************************************** +StrAllocFromError - returns the string for a particular error. + +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFromError( + __inout LPWSTR *ppwzMessage, + __in HRESULT hrError, + __in_opt HMODULE hModule, + ... + ) +{ + HRESULT hr = S_OK; + DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM; + LPVOID pvMessage = NULL; + DWORD cchMessage = 0; + + if (hModule) + { + dwFlags |= FORMAT_MESSAGE_FROM_HMODULE; + } + + va_list args; + va_start(args, hModule); + cchMessage = ::FormatMessageW(dwFlags, static_cast(hModule), hrError, 0, reinterpret_cast(&pvMessage), 0, &args); + va_end(args); + + if (0 == cchMessage) + { + ExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError); + } + + hr = StrAllocString(ppwzMessage, reinterpret_cast(pvMessage), cchMessage); + ExitOnFailure(hr, "Failed to allocate string for message."); + +LExit: + if (pvMessage) + { + ::LocalFree(pvMessage); + } + + return hr; +} + + +/******************************************************************** +StrMaxLength - returns maximum number of characters that can be stored in dynamic string p + +NOTE: assumes Unicode string +********************************************************************/ +extern "C" HRESULT DAPI StrMaxLength( + __in LPCVOID p, + __out DWORD_PTR* pcch + ) +{ + Assert(pcch); + + HRESULT hr = S_OK; + + if (p) + { + *pcch = MemSize(p); // get size of entire buffer + if (-1 == *pcch) + { + ExitFunction1(hr = E_FAIL); + } + + *pcch /= sizeof(WCHAR); // reduce to count of characters + } + else + { + *pcch = 0; + } + Assert(S_OK == hr); + +LExit: + return hr; +} + + +/******************************************************************** +StrSize - returns count of bytes in dynamic string p + +********************************************************************/ +extern "C" HRESULT DAPI StrSize( + __in LPCVOID p, + __out DWORD_PTR* pcb + ) +{ + Assert(p && pcb); + + HRESULT hr = S_OK; + + *pcb = MemSize(p); + if (-1 == *pcb) + { + hr = E_FAIL; + } + + return hr; +} + +/******************************************************************** +StrFree - releases dynamic string memory allocated by any StrAlloc*() functions + +********************************************************************/ +extern "C" HRESULT DAPI StrFree( + __in LPVOID p + ) +{ + Assert(p); + + HRESULT hr = MemFree(p); + ExitOnFailure(hr, "failed to free string"); + +LExit: + return hr; +} + + +/**************************************************************************** +StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString. +Replaces all instances. + +****************************************************************************/ +extern "C" HRESULT DAPI StrReplaceStringAll( + __inout LPWSTR* ppwzOriginal, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ) +{ + HRESULT hr = S_OK; + DWORD dwStartIndex = 0; + + do + { + hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString); + ExitOnFailure(hr, "Failed to replace substring"); + } + while (S_OK == hr); + + hr = (0 == dwStartIndex) ? S_FALSE : S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString. +Search for old substring starts at dwStartIndex. Does only 1 replace. + +****************************************************************************/ +extern "C" HRESULT DAPI StrReplaceString( + __inout LPWSTR* ppwzOriginal, + __inout DWORD* pdwStartIndex, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ) +{ + Assert(ppwzOriginal && wzOldSubString && wzNewSubString); + + HRESULT hr = S_FALSE; + LPCWSTR wzSubLocation = NULL; + LPWSTR pwzBuffer = NULL; + + if (!*ppwzOriginal) + { + ExitFunction(); + } + + wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString); + if (!wzSubLocation) + { + ExitFunction(); + } + + hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex); + ExitOnFailure(hr, "Failed to diff pointers."); + + hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal); + ExitOnFailure(hr, "Failed to duplicate string."); + + pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0'; + + hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0); + ExitOnFailure(hr, "Failed to append new string."); + + hr = StrAllocConcat(&pwzBuffer, wzSubLocation + wcslen(wzOldSubString), 0); + ExitOnFailure(hr, "Failed to append post string."); + + hr = StrFree(*ppwzOriginal); + ExitOnFailure(hr, "Failed to free original string."); + + *ppwzOriginal = pwzBuffer; + *pdwStartIndex = *pdwStartIndex + static_cast(wcslen(wzNewSubString)); + hr = S_OK; + +LExit: + return hr; +} + + +static inline BYTE HexCharToByte( + __in WCHAR wc + ) +{ + 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 + + BYTE b; + if (L'0' <= wc && wc <= L'9') + { + b = (BYTE)(wc - L'0'); + } + else if ('a' <= wc && wc <= 'f') + { + b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1)); + } + else // must be (L'A' <= wc && wc <= L'F') + { + b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1)); + } + + Assert(0 <= b && b <= 15); + return b; +} + + +/**************************************************************************** +StrHexEncode - converts an array of bytes to a text string + +NOTE: wzDest must have space for cbSource * 2 + 1 characters +****************************************************************************/ +extern "C" HRESULT DAPI StrHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in DWORD_PTR cbSource, + __out_ecount(cchDest) LPWSTR wzDest, + __in DWORD_PTR cchDest + ) +{ + Assert(pbSource && wzDest); + + HRESULT hr = S_OK; + DWORD i; + BYTE b; + + if (cchDest < 2 * cbSource + 1) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); + } + + for (i = 0; i < cbSource; ++i) + { + b = (*pbSource) >> 4; + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); + b = (*pbSource) & 0xF; + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); + + ++pbSource; + } + + *wzDest = 0; + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocHexEncode - converts an array of bytes to an allocated text string + +****************************************************************************/ +HRESULT DAPI StrAllocHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in DWORD_PTR cbSource, + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest + ) +{ + HRESULT hr = S_OK; + DWORD_PTR cchSource = sizeof(WCHAR) * (cbSource + 1); + + hr = StrAlloc(ppwzDest, cchSource); + ExitOnFailure(hr, "Failed to allocate hex string."); + + hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource); + ExitOnFailure(hr, "Failed to encode hex string."); + +LExit: + return hr; +} + + +/**************************************************************************** +StrHexDecode - converts a string of text to array of bytes + +NOTE: wzSource must contain even number of characters +****************************************************************************/ +extern "C" HRESULT DAPI StrHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(cbDest) BYTE* pbDest, + __in DWORD_PTR cbDest + ) +{ + Assert(wzSource && pbDest); + + HRESULT hr = S_OK; + DWORD cchSource = lstrlenW(wzSource); + DWORD i; + BYTE b; + + Assert(0 == cchSource % 2); + if (cbDest < cchSource / 2) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %u into %u bytes.", wzSource, cchSource, cbDest); + } + + for (i = 0; i < cchSource / 2; ++i) + { + b = HexCharToByte(*wzSource++); + (*pbDest) = b << 4; + + b = HexCharToByte(*wzSource++); + (*pbDest) |= b & 0xF; + + ++pbDest; + } + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocHexDecode - allocates a byte array hex-converted from string of text + +NOTE: wzSource must contain even number of characters +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(*pcbDest) BYTE** ppbDest, + __out_opt DWORD* pcbDest + ) +{ + Assert(wzSource && *wzSource && ppbDest); + + HRESULT hr = S_OK; + size_t cch = 0; + BYTE* pb = NULL; + DWORD cb = 0; + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch); + ExitOnFailure(hr, "Failed to calculate length of source string."); + + if (cch % 2) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded."); + } + + cb = static_cast(cch / 2); + pb = static_cast(MemAlloc(cb, TRUE)); + ExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode."); + + hr = StrHexDecode(wzSource, pb, cb); + ExitOnFailure(hr, "Failed to decode source string."); + + *ppbDest = pb; + pb = NULL; + + if (pcbDest) + { + *pcbDest = cb; + } + +LExit: + ReleaseMem(pb); + + return hr; +} + + +/**************************************************************************** +Base85 encoding/decoding data + +****************************************************************************/ +const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"; + +const BYTE Base85DecodeTable[256] = +{ + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54, + 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85 +}; + +const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 }; + + +/**************************************************************************** +StrAllocBase85Encode - converts an array of bytes into an XML compatible string + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocBase85Encode( + __in_bcount_opt(cbSource) const BYTE* pbSource, + __in DWORD_PTR cbSource, + __deref_out_z LPWSTR* pwzDest + ) +{ + HRESULT hr = S_OK; + DWORD_PTR cchDest = 0; + LPWSTR wzDest; + DWORD_PTR iSource = 0; + DWORD_PTR iDest = 0; + + if (!pwzDest || !pbSource) + { + return E_INVALIDARG; + } + + // calc actual size of output + cchDest = cbSource / 4; + cchDest += cchDest * 4; + if (cbSource & 3) + { + cchDest += (cbSource & 3) + 1; + } + ++cchDest; // add room for null terminator + + hr = StrAlloc(pwzDest, cchDest); + ExitOnFailure(hr, "failed to allocate destination string"); + + wzDest = *pwzDest; + + // first, encode full words + for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5) + { + DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24); + DWORD k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest] = Base85EncodeTable[n - k * 85]; + n = k / 85; + + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 1] = Base85EncodeTable[k - n * 85]; + k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 2] = Base85EncodeTable[n - k * 85]; + n = k / 85; + + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 3] = Base85EncodeTable[k - n * 85]; + + __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85); + + //Assert(0 <= n && n < countof(Base85EncodeTable)); + wzDest[iDest + 4] = Base85EncodeTable[n]; + } + + // encode any remaining bytes + if (iSource < cbSource) + { + DWORD n = 0; + for (DWORD i = 0; iSource + i < cbSource; ++i) + { + n += pbSource[iSource + i] << (i << 3); + } + + for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest) + { + DWORD k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest] = Base85EncodeTable[n - k * 85]; + + n = k; + } + + wzDest[iDest] = Base85EncodeTable[n]; + ++iDest; + } + Assert(iSource == cbSource); + Assert(iDest == cchDest - 1); + + wzDest[iDest] = L'\0'; + hr = S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocBase85Decode - converts a string of text to array of bytes + +NOTE: Use MemFree() to release the allocated stream of bytes +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocBase85Decode( + __in_z LPCWSTR wzSource, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out DWORD_PTR* pcbDest + ) +{ + HRESULT hr = S_OK; + DWORD_PTR cchSource = lstrlenW(wzSource); + DWORD_PTR i, n, k; + + BYTE* pbDest; + DWORD_PTR cbDest; + + if (!wzSource || !ppbDest || !pcbDest) + { + return E_INVALIDARG; + } + + // evaluate size of output and check it + k = cchSource / 5; + cbDest = k << 2; + k = cchSource - k * 5; + if (k) + { + if (1 == k) + { + // decode error -- encoded size cannot equal 1 mod 5 + return E_UNEXPECTED; + } + + cbDest += k - 1; + } + + *ppbDest = static_cast(MemAlloc(cbDest, FALSE)); + ExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string"); + + pbDest = *ppbDest; + *pcbDest = cbDest; + + // decode full words first + while (5 <= cchSource) + { + k = Base85DecodeTable[wzSource[0]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n = k; + + k = Base85DecodeTable[wzSource[1]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * 85; + + k = Base85DecodeTable[wzSource[2]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * (85 * 85); + + k = Base85DecodeTable[wzSource[3]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * (85 * 85 * 85); + + k = Base85DecodeTable[wzSource[4]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + k *= (85 * 85 * 85 * 85); + + // if (k + n > (1u << 32)) <=> (k > ~n) then decode error + if (k > ~n) + { + // overflow + return E_UNEXPECTED; + } + + n += k; + + pbDest[0] = (BYTE) n; + pbDest[1] = (BYTE) (n >> 8); + pbDest[2] = (BYTE) (n >> 16); + pbDest[3] = (BYTE) (n >> 24); + + wzSource += 5; + pbDest += 4; + cchSource -= 5; + } + + if (cchSource) + { + n = 0; + for (i = 0; i < cchSource; ++i) + { + k = Base85DecodeTable[wzSource[i]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + + n += k * Base85PowerTable[i]; + } + + for (i = 1; i < cchSource; ++i) + { + *pbDest++ = (BYTE)n; + n >>= 8; + } + + if (0 != n) + { + // decode error + return E_UNEXPECTED; + } + } + + hr = S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +MultiSzLen - calculates the length of a MULTISZ string including all nulls +including the double null terminator at the end of the MULTISZ. + +NOTE: returns 0 if the multisz in not properly terminated with two nulls +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzLen( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __out DWORD_PTR* pcch + ) +{ + Assert(pcch); + + HRESULT hr = S_OK; + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwMaxSize = 0; + + hr = StrMaxLength(pwzMultiSz, &dwMaxSize); + ExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length"); + + *pcch = 0; + while (*pcch < dwMaxSize) + { + if (L'\0' == *wz && L'\0' == *(wz + 1)) + { + break; + } + + ++wz; + *pcch = *pcch + 1; + } + + // Add two for the last 2 NULLs (that we looked ahead at) + *pcch = *pcch + 2; + + // If we've walked off the end then the length is 0 + if (*pcch > dwMaxSize) + { + *pcch = 0; + } + +LExit: + return hr; +} + + +/**************************************************************************** +MultiSzPrepend - prepends a string onto the front of a MUTLISZ + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzPrepend( + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt DWORD_PTR *pcchMultiSz, + __in __nullnullterminated LPCWSTR pwzInsert + ) +{ + Assert(ppwzMultiSz && pwzInsert && *pwzInsert); + + HRESULT hr =S_OK; + LPWSTR pwzResult = NULL; + DWORD_PTR cchResult = 0; + DWORD_PTR cchInsert = 0; + DWORD_PTR cchMultiSz = 0; + + // Get the lengths of the MULTISZ (and prime it if it's not initialized) + if (pcchMultiSz && 0 != *pcchMultiSz) + { + cchMultiSz = *pcchMultiSz; + } + else + { + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + ExitOnFailure(hr, "failed to get length of multisz"); + } + + cchInsert = lstrlenW(pwzInsert); + + cchResult = cchInsert + cchMultiSz + 1; + + // Allocate the result buffer + hr = StrAlloc(&pwzResult, cchResult + 1); + ExitOnFailure(hr, "failed to allocate result string"); + + // Prepend + hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert); + ExitOnFailure(hr, "failed to copy prepend string: %ls", pwzInsert); + + // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in + if (0 == cchMultiSz) + { + pwzResult[cchResult] = L'\0'; + ++cchResult; + } + else + { + // Copy the rest + ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR)); + + // Free the old buffer + ReleaseNullStr(*ppwzMultiSz); + } + + // Set the result + *ppwzMultiSz = pwzResult; + + if (pcchMultiSz) + { + *pcchMultiSz = cchResult; + } + + pwzResult = NULL; + +LExit: + ReleaseNullStr(pwzResult); + + return hr; +} + +/**************************************************************************** +MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the +specified sub string and returns the index of the +string in the MULTISZ, the address, neither, or both + +NOTE: returns S_FALSE if the string is not found +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzFindSubstring( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzSubstring, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn + ) +{ + Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring); + + HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty) + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwIndex = 0; + DWORD_PTR cchMultiSz = 0; + DWORD_PTR cchProgress = 0; + + hr = MultiSzLen(pwzMultiSz, &cchMultiSz); + ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the string containing the sub string + hr = S_OK; + while (NULL == wcsistr(wz, pwzSubstring)) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++dwIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found it give them what they want + if (S_OK == hr) + { + if (pdwIndex) + { + *pdwIndex = dwIndex; + } + + if (ppwzFoundIn) + { + *ppwzFoundIn = wz; + } + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzFindString - finds a string in a MULTISZ and returns the index of +the string in the MULTISZ, the address or both + +NOTE: returns S_FALSE if the string is not found +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzFindString( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzString, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound + ) +{ + Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound)); + + HRESULT hr = S_FALSE; // Assume we won't find it + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwIndex = 0; + DWORD_PTR cchMutliSz = 0; + DWORD_PTR cchProgress = 0; + + hr = MultiSzLen(pwzMultiSz, &cchMutliSz); + ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the string + hr = S_OK; + while (0 != lstrcmpW(wz, pwzString)) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMutliSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++dwIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found it give them what they want + if (S_OK == hr) + { + if (pdwIndex) + { + *pdwIndex = dwIndex; + } + + if (ppwzFound) + { + *ppwzFound = wz; + } + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzRemoveString - removes string from a MULTISZ at the specified +index + +NOTE: does an in place removal without shrinking the memory allocation + +NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzRemoveString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex + ) +{ + Assert(ppwzMultiSz && *ppwzMultiSz); + + HRESULT hr = S_OK; + LPCWSTR wz = *ppwzMultiSz; + LPCWSTR wzNext = NULL; + DWORD_PTR dwCurrentIndex = 0; + DWORD_PTR cchMultiSz = 0; + DWORD_PTR cchProgress = 0; + + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the index we want to remove + hr = S_OK; + while (dwCurrentIndex < dwIndex) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++cchProgress; + ++dwCurrentIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found the index to be removed + if (S_OK == hr) + { + wzNext = wz; + + // Slide through to the end of the current string + while (L'\0' != *wzNext && cchProgress < cchMultiSz) + { + ++wzNext; + ++cchProgress; + } + + // Something weird has happened if we're past the end of the MULTISZ + if (cchProgress > cchMultiSz) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ"); + } + + // Move on to the next character + ++wzNext; + ++cchProgress; + + ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR)); + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzInsertString - inserts new string at the specified index + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzInsertString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt DWORD_PTR *pcchMultiSz, + __in DWORD_PTR dwIndex, + __in __nullnullterminated LPCWSTR pwzInsert + ) +{ + Assert(ppwzMultiSz && pwzInsert && *pwzInsert); + + HRESULT hr = S_OK; + LPCWSTR wz = *ppwzMultiSz; + DWORD_PTR dwCurrentIndex = 0; + DWORD_PTR cchProgress = 0; + LPWSTR pwzResult = NULL; + DWORD_PTR cchResult = 0; + DWORD_PTR cchString = lstrlenW(pwzInsert); + DWORD_PTR cchMultiSz = 0; + + if (pcchMultiSz && 0 != *pcchMultiSz) + { + cchMultiSz = *pcchMultiSz; + } + else + { + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + } + + // Find the index we want to insert at + hr = S_OK; + while (dwCurrentIndex < dwIndex) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz) + { + hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND); + ExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex); + } + + // Move on to the next string + ++wz; + ++cchProgress; + ++dwCurrentIndex; + } + + // + // Insert the string + // + cchResult = cchMultiSz + cchString + 1; + + hr = StrAlloc(&pwzResult, cchResult); + ExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); + + // Copy the part before the insert + ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR)); + + // Copy the insert part + ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR)); + + // Copy the part after the insert + ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR)); + + // Free the old buffer + ReleaseNullStr(*ppwzMultiSz); + + // Set the result + *ppwzMultiSz = pwzResult; + + // If they wanted the resulting length, let 'em have it + if (pcchMultiSz) + { + *pcchMultiSz = cchResult; + } + + pwzResult = NULL; + +LExit: + ReleaseStr(pwzResult); + + return hr; +} + +/**************************************************************************** +MultiSzReplaceString - replaces string at the specified index with a new one + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzReplaceString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex, + __in __nullnullterminated LPCWSTR pwzString + ) +{ + Assert(ppwzMultiSz && pwzString && *pwzString); + + HRESULT hr = S_OK; + + hr = MultiSzRemoveString(ppwzMultiSz, dwIndex); + ExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex); + + hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString); + ExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex); + +LExit: + return hr; +} + + +/**************************************************************************** +wcsistr - case insensitive find a substring + +****************************************************************************/ +extern "C" LPCWSTR DAPI wcsistr( + __in_z LPCWSTR wzString, + __in_z LPCWSTR wzCharSet + ) +{ + LPCWSTR wzSource = wzString; + LPCWSTR wzSearch = NULL; + DWORD_PTR cchSourceIndex = 0; + + // Walk through wzString (the source string) one character at a time + while (*wzSource) + { + cchSourceIndex = 0; + wzSearch = wzCharSet; + + // Look ahead in the source string until we get a full match or we hit the end of the source + while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch)) + { + ++cchSourceIndex; + ++wzSearch; + } + + // If we found it, return the point that we found it at + if (L'\0' == *wzSearch) + { + return wzSource; + } + + // Walk ahead one character + ++wzSource; + } + + return NULL; +} + +/**************************************************************************** +StrStringToInt16 - converts a string to a signed 16-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out SHORT* psOut + ) +{ + HRESULT hr = S_OK; + LONGLONG ll = 0; + + hr = StrStringToInt64(wzIn, cchIn, &ll); + ExitOnFailure(hr, "Failed to parse int64."); + + if (SHORT_MAX < ll || SHORT_MIN > ll) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *psOut = (SHORT)ll; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt16 - converts a string to an unsigned 16-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out USHORT* pusOut + ) +{ + HRESULT hr = S_OK; + ULONGLONG ull = 0; + + hr = StrStringToUInt64(wzIn, cchIn, &ull); + ExitOnFailure(hr, "Failed to parse uint64."); + + if (USHORT_MAX < ull) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *pusOut = (USHORT)ull; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToInt32 - converts a string to a signed 32-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out INT* piOut + ) +{ + HRESULT hr = S_OK; + LONGLONG ll = 0; + + hr = StrStringToInt64(wzIn, cchIn, &ll); + ExitOnFailure(hr, "Failed to parse int64."); + + if (INT_MAX < ll || INT_MIN > ll) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *piOut = (INT)ll; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt32 - converts a string to an unsigned 32-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out UINT* puiOut + ) +{ + HRESULT hr = S_OK; + ULONGLONG ull = 0; + + hr = StrStringToUInt64(wzIn, cchIn, &ull); + ExitOnFailure(hr, "Failed to parse uint64."); + + if (UINT_MAX < ull) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *puiOut = (UINT)ull; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToInt64 - converts a string to a signed 64-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out LONGLONG* pllOut + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + INT iSign = 1; + INT nDigit = 0; + LARGE_INTEGER liValue = { }; + + // get string length if not provided + if (0 >= cchIn) + { + cchIn = lstrlenW(wzIn); + if (0 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + // check sign + if (L'-' == wzIn[0]) + { + if (1 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + i = 1; + iSign = -1; + } + + // read digits + while (i < cchIn) + { + nDigit = wzIn[i] - L'0'; + if (0 > nDigit || 9 < nDigit) + { + ExitFunction1(hr = E_INVALIDARG); + } + liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign; + + if ((liValue.HighPart ^ iSign) & INT_MIN) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + ++i; + } + + *pllOut = liValue.QuadPart; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt64 - converts a string to an unsigned 64-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out ULONGLONG* pullOut + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + DWORD nDigit = 0; + ULONGLONG ullValue = 0; + ULONGLONG ull = 0; + + // get string length if not provided + if (0 >= cchIn) + { + cchIn = lstrlenW(wzIn); + if (0 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + // read digits + while (i < cchIn) + { + nDigit = wzIn[i] - L'0'; + if (9 < nDigit) + { + ExitFunction1(hr = E_INVALIDARG); + } + ull = (ULONGLONG)(ullValue * 10 + nDigit); + + if (ull < ullValue) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + ullValue = ull; + ++i; + } + + *pullOut = ullValue; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUpper - alters the given string in-place to be entirely uppercase + +****************************************************************************/ +void DAPI StrStringToUpper( + __inout_z LPWSTR wzIn + ) +{ + ::CharUpperBuffW(wzIn, lstrlenW(wzIn)); +} + +/**************************************************************************** +StrStringToLower - alters the given string in-place to be entirely lowercase + +****************************************************************************/ +void DAPI StrStringToLower( + __inout_z LPWSTR wzIn + ) +{ + ::CharLowerBuffW(wzIn, lstrlenW(wzIn)); +} + +/**************************************************************************** +StrAllocStringToUpperInvariant - creates an upper-case copy of a string. + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocStringToUpperInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in int cchSource + ) +{ + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE); +} + +/**************************************************************************** +StrAllocStringToLowerInvariant - creates an lower-case copy of a string. + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocStringToLowerInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in int cchSource + ) +{ + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE); +} + +/**************************************************************************** +StrArrayAllocString - Allocates a string array. + +****************************************************************************/ +extern "C" HRESULT DAPI StrArrayAllocString( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + HRESULT hr = S_OK; + UINT cNewStrArray; + + hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray); + ExitOnFailure(hr, "Failed to increment the string array element count."); + + hr = MemEnsureArraySize(reinterpret_cast(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE); + ExitOnFailure(hr, "Failed to allocate memory for the string array."); + + hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource); + ExitOnFailure(hr, "Failed to allocate and assign the string."); + + *pcStrArray = cNewStrArray; + +LExit: + return hr; +} + +/**************************************************************************** +StrArrayFree - Frees a string array. + +Use ReleaseNullStrArray to nullify the arguments. + +****************************************************************************/ +extern "C" HRESULT DAPI StrArrayFree( + __in_ecount(cStrArray) LPWSTR *rgsczStrArray, + __in UINT cStrArray + ) +{ + HRESULT hr = S_OK; + + for (UINT i = 0; i < cStrArray; ++i) + { + if (NULL != rgsczStrArray[i]) + { + hr = StrFree(rgsczStrArray[i]); + ExitOnFailure(hr, "Failed to free the string at index %u.", i); + } + } + + hr = MemFree(rgsczStrArray); + ExitOnFailure(hr, "Failed to free memory for the string array."); + +LExit: + return hr; +} + +/**************************************************************************** +StrSplitAllocArray - Splits a string into an array. + +****************************************************************************/ +extern "C" HRESULT DAPI StrSplitAllocArray( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzDelim + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCopy = NULL; + LPWSTR wzContext = NULL; + + // Copy wzSource so it is not modified. + hr = StrAllocString(&sczCopy, wzSource, 0); + ExitOnFailure(hr, "Failed to copy the source string."); + + for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) + { + hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0); + ExitOnFailure(hr, "Failed to add the string to the string array."); + } + +LExit: + ReleaseStr(sczCopy); + + return hr; +} + +/**************************************************************************** +StrAllocStringMapInvariant - helper function for the ToUpper and ToLower. + +Note: Assumes source and destination buffers will be the same. +****************************************************************************/ +static HRESULT StrAllocStringMapInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in int cchSource, + __in DWORD dwMapFlags + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(pscz, wzSource, cchSource); + ExitOnFailure(hr, "Failed to allocate a copy of the source string."); + + if (0 == cchSource) + { + // Need the actual string size for LCMapString. This includes the null-terminator + // but LCMapString doesn't care either way. + hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast(&cchSource)); + ExitOnFailure(hr, "Failed to get the length of the string."); + } + + // Convert the copy of the string to upper or lower case in-place. + if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, cchSource, *pscz, cchSource)) + { + ExitWithLastError(hr, "Failed to convert the string case."); + } + +LExit: + return hr; +} + +/**************************************************************************** +StrSecureZeroString - zeroes out string to the make sure the contents +don't remain in memory. + +****************************************************************************/ +extern "C" DAPI_(HRESULT) StrSecureZeroString( + __in LPWSTR pwz + ) +{ + HRESULT hr = S_OK; + DWORD_PTR cch; + + if (pwz) + { + cch = MemSize(pwz); + if (-1 == cch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to get size of string"); + } + else + { + SecureZeroMemory(pwz, cch); + } + } + +LExit: + return hr; +} + +/**************************************************************************** +StrSecureZeroFreeString - zeroes out string to the make sure the contents +don't remain in memory, then frees the string. + +****************************************************************************/ +extern "C" DAPI_(HRESULT) StrSecureZeroFreeString( + __in LPWSTR pwz + ) +{ + HRESULT hr = S_OK; + + hr = StrSecureZeroString(pwz); + ReleaseStr(pwz); + + return hr; +} diff --git a/src/dutil/svcutil.cpp b/src/dutil/svcutil.cpp new file mode 100644 index 00000000..9da2b5b3 --- /dev/null +++ b/src/dutil/svcutil.cpp @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** +SvcQueryConfig - queries the configuration of a service + +********************************************************************/ +extern "C" HRESULT DAPI SvcQueryConfig( + __in SC_HANDLE sch, + __out QUERY_SERVICE_CONFIGW** ppConfig + ) +{ + HRESULT hr = S_OK; + QUERY_SERVICE_CONFIGW* pConfig = NULL; + DWORD cbConfig = 0; + + if (!::QueryServiceConfigW(sch, NULL, 0, &cbConfig)) + { + DWORD er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + pConfig = static_cast(MemAlloc(cbConfig, TRUE)); + ExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration."); + + if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig)) + { + ExitWithLastError(hr, "Failed to read service configuration."); + } + } + else + { + ExitOnWin32Error(er, hr, "Failed to query service configuration."); + } + } + + *ppConfig = pConfig; + pConfig = NULL; + +LExit: + ReleaseMem(pConfig); + + return hr; +} diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp new file mode 100644 index 00000000..cae92d92 --- /dev/null +++ b/src/dutil/thmutil.cpp @@ -0,0 +1,5226 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +#ifndef BS_COMMANDLINK +#define BS_COMMANDLINK 0x0000000EL +#endif + +#ifndef BCM_SETNOTE +#define BCM_SETNOTE (BCM_FIRST + 0x0009) +#endif + +#ifndef BCM_SETSHIELD +#define BCM_SETSHIELD (BCM_FIRST + 0x000C) +#endif + +#ifndef LWS_NOPREFIX +#define LWS_NOPREFIX 0x0004 +#endif + +const DWORD THEME_INVALID_ID = 0xFFFFFFFF; +const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF; +const DWORD GROW_WINDOW_TEXT = 250; +const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink"; +const LPCWSTR THEME_WC_PANEL = L"ThemePanel"; + +static Gdiplus::GdiplusStartupInput vgsi; +static Gdiplus::GdiplusStartupOutput vgso = { }; +static ULONG_PTR vgdiToken = 0; +static ULONG_PTR vgdiHookToken = 0; +static HMODULE vhHyperlinkRegisteredModule = NULL; +static HMODULE vhPanelRegisteredModule = NULL; +static HMODULE vhModuleRichEd = NULL; +static HCURSOR vhCursorHand = NULL; + +enum INTERNAL_CONTROL_STYLE +{ + INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001, + INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002, + INTERNAL_CONTROL_STYLE_DISABLED = 0x0004, + INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008, + INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010, +}; + +struct MEMBUFFER_FOR_RICHEDIT +{ + BYTE* rgbData; + DWORD cbData; + + DWORD iData; +}; + + +// prototypes +static HRESULT RegisterWindowClasses( + __in_opt HMODULE hModule + ); +static HRESULT ParseTheme( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMDocument* pixd, + __out THEME** ppTheme + ); +static HRESULT ParseImage( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HBITMAP* phImage + ); +static HRESULT ParseIcon( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HICON* phIcon + ); +static HRESULT ParseWindow( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ); +static HRESULT GetFontColor( + __in IXMLDOMNode* pixn, + __in_z LPCWSTR wzAttributeName, + __out COLORREF* pColorRef, + __out DWORD* pdwSystemColor + ); +static HRESULT ParseFonts( + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ); +static HRESULT ParsePages( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ); +static HRESULT ParseImageLists( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ); +static HRESULT ParseControls( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_opt THEME_PAGE* pPage + ); +static HRESULT ParseControl( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in BOOL fSkipDimensions, + __in_opt THEME_PAGE* pPage + ); +static HRESULT ParseActions( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseColumns( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseRadioButtons( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in THEME_PAGE* pPage + ); +static HRESULT ParseTabs( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseText( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren +); +static HRESULT ParseTooltips( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren + ); +static HRESULT ParseNotes( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __out BOOL* pfAnyChildren + ); +static HRESULT StopBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ); +static HRESULT StartBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ); +static HRESULT FindImageList( + __in THEME* pTheme, + __in_z LPCWSTR wzImageListName, + __out HIMAGELIST *phImageList + ); +static HRESULT LoadControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in HWND hwndParent, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ); +static HRESULT ShowControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId, + __out_opt HWND* phwndFocus + ); +static HRESULT ShowControls( + __in THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId + ); +static HRESULT DrawButton( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static void DrawControlText( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl, + __in BOOL fCentered, + __in BOOL fDrawFocusRect + ); +static HRESULT DrawHyperlink( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static HRESULT DrawImage( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static HRESULT DrawProgressBar( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static BOOL DrawHoverControl( + __in THEME* pTheme, + __in BOOL fHover + ); +static DWORD CALLBACK RichEditStreamFromFileHandleCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG *pcb + ); +static DWORD CALLBACK RichEditStreamFromMemoryCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG *pcb + ); +static void FreeFont( + __in THEME_FONT* pFont + ); +static void FreePage( + __in THEME_PAGE* pPage + ); +static void FreeControl( + __in THEME_CONTROL* pControl + ); +static void FreeConditionalText( + __in THEME_CONDITIONAL_TEXT* pConditionalText + ); +static void FreeImageList( + __in THEME_IMAGELIST* pImageList + ); +static void FreeAction( + __in THEME_ACTION* pAction + ); +static void FreeColumn( + __in THEME_COLUMN* pColumn + ); +static void FreeTab( + __in THEME_TAB* pTab + ); +static void CALLBACK OnBillboardTimer( + __in THEME* pTheme, + __in HWND hwnd, + __in UINT_PTR idEvent + ); +static void OnBrowseDirectory( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_ACTION* pAction + ); +static BOOL OnButtonClicked( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_CONTROL* pControl + ); +static HRESULT OnRichEditEnLink( + __in LPARAM lParam, + __in HWND hWndRichEdit, + __in HWND hWnd + ); +static BOOL ControlIsType( + __in const THEME* pTheme, + __in DWORD dwControl, + __in THEME_CONTROL_TYPE type + ); +static const THEME_CONTROL* FindControlFromHWnd( + __in const THEME* pTheme, + __in HWND hWnd, + __in_opt const THEME_CONTROL* pParentControl = NULL + ); +static void GetControlDimensions( + __in const RECT* prcParent, + __in const THEME_CONTROL* pControl, + __out int* piWidth, + __out int* piHeight, + __out int* piX, + __out int* piY + ); +// Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns +// calculates final width of each column (storing result in each column's nWidth value) +static HRESULT SizeListViewColumns( + __inout THEME_CONTROL* pControl + ); +static LRESULT CALLBACK PanelWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static HRESULT LocalizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const WIX_LOCALIZATION *pWixLoc + ); +static HRESULT LocalizeControl( + __in THEME_CONTROL* pControl, + __in const WIX_LOCALIZATION *pWixLoc + ); +static HRESULT LoadControlsString( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in HMODULE hResModule + ); +static HRESULT LoadControlString( + __in THEME_CONTROL* pControl, + __in HMODULE hResModule + ); +static void ResizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const RECT* prcParent + ); +static void ResizeControl( + __in THEME_CONTROL* pControl, + __in const RECT* prcParent + ); +static void GetControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __out DWORD** ppcControls, + __out THEME_CONTROL*** pprgControls + ); +static void GetControls( + __in const THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __out DWORD& cControls, + __out THEME_CONTROL*& rgControls + ); +static void UnloadControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls + ); + + +// Public functions. + +DAPI_(HRESULT) ThemeInitialize( + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + INITCOMMONCONTROLSEX icex = { }; + + hr = XmlInitialize(); + ExitOnFailure(hr, "Failed to initialize XML."); + + hr = RegisterWindowClasses(hModule); + ExitOnFailure(hr, "Failed to register theme window classes."); + + // Initialize GDI+ and common controls. + vgsi.SuppressBackgroundThread = TRUE; + + hr = GdipInitialize(&vgsi, &vgdiToken, &vgso); + ExitOnFailure(hr, "Failed to initialize GDI+."); + + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS; + ::InitCommonControlsEx(&icex); + + (*vgso.NotificationHook)(&vgdiHookToken); + +LExit: + return hr; +} + + +DAPI_(void) ThemeUninitialize() +{ + if (vhModuleRichEd) + { + ::FreeLibrary(vhModuleRichEd); + vhModuleRichEd = NULL; + } + + if (vhHyperlinkRegisteredModule) + { + ::UnregisterClassW(THEME_WC_HYPERLINK, vhHyperlinkRegisteredModule); + vhHyperlinkRegisteredModule = NULL; + } + + if (vhPanelRegisteredModule) + { + ::UnregisterClassW(THEME_WC_PANEL, vhPanelRegisteredModule); + vhPanelRegisteredModule = NULL; + } + + if (vgdiToken) + { + GdipUninitialize(vgdiToken); + vgdiToken = 0; + } + + XmlUninitialize(); +} + + +DAPI_(HRESULT) ThemeLoadFromFile( + __in_z LPCWSTR wzThemeFile, + __out THEME** ppTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixd = NULL; + LPWSTR sczRelativePath = NULL; + + hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd); + ExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = PathGetDirectory(wzThemeFile, &sczRelativePath); + ExitOnFailure(hr, "Failed to get relative path from theme file."); + + hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme); + ExitOnFailure(hr, "Failed to parse theme."); + +LExit: + ReleaseStr(sczRelativePath); + ReleaseObject(pixd); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadFromResource( + __in_opt HMODULE hModule, + __in_z LPCSTR szResource, + __out THEME** ppTheme + ) +{ + HRESULT hr = S_OK; + LPVOID pvResource = NULL; + DWORD cbResource = 0; + LPWSTR sczXml = NULL; + IXMLDOMDocument* pixd = NULL; + + hr = ResReadData(hModule, szResource, &pvResource, &cbResource); + ExitOnFailure(hr, "Failed to read theme from resource."); + + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); + ExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + + hr = XmlLoadDocument(sczXml, &pixd); + ExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = ParseTheme(hModule, NULL, pixd, ppTheme); + ExitOnFailure(hr, "Failed to parse theme."); + +LExit: + ReleaseObject(pixd); + ReleaseStr(sczXml); + + return hr; +} + + +DAPI_(void) ThemeFree( + __in THEME* pTheme + ) +{ + if (pTheme) + { + for (DWORD i = 0; i < pTheme->cFonts; ++i) + { + FreeFont(pTheme->rgFonts + i); + } + + for (DWORD i = 0; i < pTheme->cPages; ++i) + { + FreePage(pTheme->rgPages + i); + } + + for (DWORD i = 0; i < pTheme->cImageLists; ++i) + { + FreeImageList(pTheme->rgImageLists + i); + } + + for (DWORD i = 0; i < pTheme->cControls; ++i) + { + FreeControl(pTheme->rgControls + i); + } + + ReleaseMem(pTheme->rgControls); + ReleaseMem(pTheme->rgPages); + ReleaseMem(pTheme->rgFonts); + + if (pTheme->hImage) + { + ::DeleteBitmap(pTheme->hImage); + } + + ReleaseStr(pTheme->sczCaption); + ReleaseMem(pTheme); + } +} + +DAPI_(HRESULT) ThemeRegisterVariableCallbacks( + __in THEME* pTheme, + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + ExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + + pTheme->pfnEvaluateCondition = pfnEvaluateCondition; + pTheme->pfnFormatString = pfnFormatString; + pTheme->pfnGetNumericVariable = pfnGetNumericVariable; + pTheme->pfnSetNumericVariable = pfnSetNumericVariable; + pTheme->pfnGetStringVariable = pfnGetStringVariable; + pTheme->pfnSetStringVariable = pfnSetStringVariable; + pTheme->pvVariableContext = pvContext; + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeLoadControls( + __in THEME* pTheme, + __in HWND hwndParent, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ) +{ + return LoadControls(pTheme, NULL, hwndParent, rgAssignControlIds, cAssignControlIds); +} + + +DAPI_(void) ThemeUnloadControls( + __in THEME* pTheme + ) +{ + UnloadControls(pTheme->cControls, pTheme->rgControls); + + pTheme->hwndHover = NULL; + pTheme->hwndParent = NULL; +} + +DAPI_(HRESULT) ThemeLocalize( + __in THEME *pTheme, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCaption = NULL; + + hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption); + ExitOnFailure(hr, "Failed to localize theme caption."); + + if (pTheme->pfnFormatString) + { + hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext); + if (SUCCEEDED(hr)) + { + hr = ThemeUpdateCaption(pTheme, sczCaption); + } + } + + hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc); + +LExit: + ReleaseStr(sczCaption); + + return hr; +} + +/******************************************************************** + ThemeLoadStrings - Loads string resources. + Must be called after loading a theme and before calling + ThemeLoadControls. +*******************************************************************/ +DAPI_(HRESULT) ThemeLoadStrings( + __in THEME* pTheme, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + ExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + + if (UINT_MAX != pTheme->uStringId) + { + hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption); + ExitOnFailure(hr, "Failed to load theme caption."); + } + + hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule); + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeLoadRichEditFromFile( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCWSTR wzFileName, + __in HMODULE hModule + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFile = NULL; + HANDLE hFile = INVALID_HANDLE_VALUE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + hr = PathRelativeToModule(&sczFile, wzFileName, hModule); + ExitOnFailure(hr, "Failed to read resource data."); + + hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to open RTF file."); + } + else + { + LONGLONG llRtfSize; + hr = FileSizeByHandle(hFile, &llRtfSize); + if (SUCCEEDED(hr)) + { + ::SendMessageW(hWnd, EM_EXLIMITTEXT, 0, static_cast(llRtfSize)); + } + + EDITSTREAM es = { }; + es.pfnCallback = RichEditStreamFromFileHandleCallback; + es.dwCookie = reinterpret_cast(hFile); + + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); + hr = es.dwError; + ExitOnFailure(hr, "Failed to update RTF stream."); + } + +LExit: + ReleaseStr(sczFile); + ReleaseFile(hFile); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadRichEditFromResource( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ThemeLoadRichEditFromResourceToHWnd(hWnd, szResourceName, hModule); +} + +DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( + __in HWND hWnd, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ) +{ + HRESULT hr = S_OK; + MEMBUFFER_FOR_RICHEDIT buffer = { }; + EDITSTREAM es = { }; + + hr = ResReadData(hModule, szResourceName, reinterpret_cast(&buffer.rgbData), &buffer.cbData); + ExitOnFailure(hr, "Failed to read resource data."); + + es.pfnCallback = RichEditStreamFromMemoryCallback; + es.dwCookie = reinterpret_cast(&buffer); + + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); + hr = es.dwError; + ExitOnFailure(hr, "Failed to update RTF stream."); + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeHandleKeyboardMessage( + __in_opt THEME* pTheme, + __in HWND /*hWnd*/, + __in MSG* pMsg + ) +{ + return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE; +} + + +extern "C" LRESULT CALLBACK ThemeDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + RECT rcParent = { }; + RECT *pRect = NULL; + + if (pTheme) + { + switch (uMsg) + { + case WM_NCHITTEST: + if (pTheme->dwStyle & WS_POPUP) + { + return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control. + } + break; + + case WM_WINDOWPOSCHANGED: + { + //WINDOWPOS* pos = reinterpret_cast(lParam); + //ThemeWindowPositionChanged(pTheme, pos); + } + break; + + case WM_DRAWITEM: + ThemeDrawControl(pTheme, reinterpret_cast(lParam)); + return TRUE; + + case WM_CTLCOLORBTN: __fallthrough; + case WM_CTLCOLORSTATIC: + { + HBRUSH hBrush = NULL; + if (ThemeSetControlColor(pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam), &hBrush)) + { + return reinterpret_cast(hBrush); + } + } + break; + + case WM_SETCURSOR: + if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast(wParam))) + { + return TRUE; + } + break; + + case WM_PAINT: + if (::GetUpdateRect(hWnd, NULL, FALSE)) + { + PAINTSTRUCT ps; + ::BeginPaint(hWnd, &ps); + if (hWnd == pTheme->hwndParent) + { + ThemeDrawBackground(pTheme, &ps); + } + ::EndPaint(hWnd, &ps); + } + return 0; + + case WM_SIZING: + if (pTheme->fAutoResize) + { + pRect = reinterpret_cast(lParam); + if (pRect->right - pRect->left < pTheme->nMinimumWidth) + { + if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) + { + pRect->left = pRect->right - pTheme->nMinimumWidth; + } + else + { + pRect->right = pRect->left + pTheme->nMinimumWidth; + } + } + if (pRect->bottom - pRect->top < pTheme->nMinimumHeight) + { + if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT) + { + pRect->bottom = pRect->top + pTheme->nMinimumHeight; + } + else + { + pRect->top = pRect->bottom - pTheme->nMinimumHeight; + } + } + + return TRUE; + } + break; + + case WM_SIZE: + if (pTheme->fAutoResize) + { + ::GetClientRect(pTheme->hwndParent, &rcParent); + ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent); + return 0; + } + break; + + case WM_TIMER: + OnBillboardTimer(pTheme, hWnd, wParam); + break; + + case WM_NOTIFY: + if (lParam) + { + LPNMHDR pnmhdr = reinterpret_cast(lParam); + switch (pnmhdr->code) + { + // Tab/Shift+Tab support for rich-edit control. + case EN_MSGFILTER: + { + MSGFILTER* msgFilter = reinterpret_cast(lParam); + if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam) + { + BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT); + HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift); + ::SetFocus(hwndFocus); + return 1; + } + break; + } + + // Hyperlink clicks from rich-edit control. + case EN_LINK: + return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd)); + + // Clicks on a hypertext/syslink control. + case NM_CLICK: __fallthrough; + case NM_RETURN: + if (ControlIsType(pTheme, static_cast(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT)) + { + PNMLINK pnmlink = reinterpret_cast(lParam); + LITEM litem = pnmlink->item; + ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); + return 1; + } + + return 0; + } + } + break; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + if (lParam) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam); + if (pControl && OnButtonClicked(pTheme, hWnd, pControl)) + { + return 0; + } + } + break; + } + break; + } + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + + +DAPI_(void) ThemeGetPageIds( + __in const THEME* pTheme, + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, + __inout_ecount(cGetPages) DWORD* rgdwPageIds, + __in DWORD cGetPages + ) +{ + for (DWORD i = 0; i < cGetPages; ++i) + { + LPCWSTR wzFindName = rgwzFindNames[i]; + for (DWORD j = 0; j < pTheme->cPages; ++j) + { + LPCWSTR wzPageName = pTheme->rgPages[j].sczName; + if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1)) + { + rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid). + break; + } + } + } +} + + +DAPI_(THEME_PAGE*) ThemeGetPage( + __in const THEME* pTheme, + __in DWORD dwPage + ) +{ + DWORD iPage = dwPage - 1; + THEME_PAGE* pPage = NULL; + + if (iPage < pTheme->cPages) + { + pPage = pTheme->rgPages + iPage; + } + + return pPage; +} + + +DAPI_(HRESULT) ThemeShowPage( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow + ) +{ + return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT); +} + + +DAPI_(HRESULT) ThemeShowPageEx( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow, + __in THEME_SHOW_PAGE_REASON reason + ) +{ + HRESULT hr = S_OK; + BOOL fHide = SW_HIDE == nCmdShow; + BOOL fSaveEditboxes = FALSE; + THEME_SAVEDVARIABLE* pSavedVariable = NULL; + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage); + + if (pPage) + { + if (fHide) + { + switch (reason) + { + case THEME_SHOW_PAGE_REASON_DEFAULT: + // Set the variables in the loop below. + fSaveEditboxes = TRUE; + break; + case THEME_SHOW_PAGE_REASON_CANCEL: + if (pPage->cSavedVariables && pTheme->pfnSetStringVariable) + { + // Best effort to cancel any changes to the variables. + for (DWORD v = 0; v < pPage->cSavedVariables; ++v) + { + pSavedVariable = pPage->rgSavedVariables + v; + if (pSavedVariable->wzName) + { + pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, pTheme->pvVariableContext); + } + } + } + break; + } + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason) + { + pPage->cSavedVariables = 0; + if (pPage->rgSavedVariables) + { + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); + } + } + + pTheme->dwCurrentPageId = 0; + } + else + { + if (THEME_SHOW_PAGE_REASON_REFRESH == reason) + { + fSaveEditboxes = TRUE; + } + else + { + hr = MemEnsureArraySize(reinterpret_cast(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices); + ExitOnNull(pPage->rgSavedVariables, hr, E_OUTOFMEMORY, "Failed to allocate memory for saved variables."); + + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); + pPage->cSavedVariables = pPage->cControlIndices; + + // Save the variables in the loop below. + } + + pTheme->dwCurrentPageId = dwPage; + } + } + + hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage); + ExitOnFailure(hr, "Failed to show page controls."); + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeControlExists( + __in const THEME* pTheme, + __in DWORD dwControl + ) +{ + BOOL fExists = FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + fExists = (pControl && hWnd == pControl->hWnd); + } + + return fExists; +} + + +DAPI_(void) ThemeControlEnable( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fEnable + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED); + ::EnableWindow(hWnd, fEnable); + + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) + { + ::ShowWindow(hWnd, fEnable ? SW_SHOW : SW_HIDE); + } + } +} + + +DAPI_(BOOL) ThemeControlEnabled( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + return pControl && !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); +} + + +DAPI_(void) ThemeControlElevates( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fElevates + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + ::SendMessageW(hWnd, BCM_SETSHIELD, 0, fElevates); +} + + +DAPI_(void) ThemeShowControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + ::ShowWindow(hWnd, nCmdShow); + + // Save the control's visible state. + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN); + } +} + + +DAPI_(void) ThemeShowControlEx( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + ShowControl(pTheme, pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL); + } +} + + +DAPI_(BOOL) ThemeControlVisible( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ::IsWindowVisible(hWnd); +} + + +DAPI_(BOOL) ThemePostControlMessage( + __in THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (!::PostMessageW(hWnd, Msg, wParam, lParam)) + { + er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + } + + return SUCCEEDED(hr); +} + + +DAPI_(LRESULT) ThemeSendControlMessage( + __in const THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ::SendMessageW(hWnd, Msg, wParam, lParam); +} + + +DAPI_(HRESULT) ThemeDrawBackground( + __in THEME* pTheme, + __in PAINTSTRUCT* pps + ) +{ + HRESULT hr = S_FALSE; + + if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase) + { + HDC hdcMem = ::CreateCompatibleDC(pps->hdc); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pTheme->hImage)); + + ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, pTheme->nWidth, pTheme->nHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + + hr = S_OK; + } + + return hr; +} + + +DAPI_(HRESULT) ThemeDrawControl( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis + ) +{ + HRESULT hr = S_OK; + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, pdis->hwndItem); + + AssertSz(pControl, "Expected control window from owner draw window."); + AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window."); + AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width."); + AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height."); + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_BUTTON: + hr = DrawButton(pTheme, pdis, pControl); + ExitOnFailure(hr, "Failed to draw button."); + break; + + case THEME_CONTROL_TYPE_HYPERLINK: + hr = DrawHyperlink(pTheme, pdis, pControl); + ExitOnFailure(hr, "Failed to draw hyperlink."); + break; + + case THEME_CONTROL_TYPE_IMAGE: + hr = DrawImage(pTheme, pdis, pControl); + ExitOnFailure(hr, "Failed to draw image."); + break; + + case THEME_CONTROL_TYPE_PROGRESSBAR: + hr = DrawProgressBar(pTheme, pdis, pControl); + ExitOnFailure(hr, "Failed to draw progress bar."); + break; + + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Did not specify an owner draw control to draw."); + } + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeHoverControl( + __in THEME* pTheme, + __in HWND hwndParent, + __in HWND hwndControl + ) +{ + BOOL fHovered = FALSE; + if (hwndControl != pTheme->hwndHover) + { + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) + { + DrawHoverControl(pTheme, FALSE); + } + + pTheme->hwndHover = hwndControl; + + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) + { + fHovered = DrawHoverControl(pTheme, TRUE); + } + } + + return fHovered; +} + + +DAPI_(BOOL) ThemeIsControlChecked( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return BST_CHECKED == ::SendMessageW(hWnd, BM_GETCHECK, 0, 0); +} + + +DAPI_(BOOL) ThemeSetControlColor( + __in THEME* pTheme, + __in HDC hdc, + __in HWND hWnd, + __out HBRUSH* phBackgroundBrush + ) +{ + THEME_FONT* pFont = NULL; + BOOL fHasBackground = FALSE; + + *phBackgroundBrush = NULL; + + if (hWnd == pTheme->hwndParent) + { + pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId; + } + else + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + pFont = (!pControl || THEME_INVALID_ID == pControl->dwFontId) ? NULL : pTheme->rgFonts + pControl->dwFontId; + } + + if (pFont) + { + if (pFont->hForeground) + { + ::SetTextColor(hdc, pFont->crForeground); + } + + if (pFont->hBackground) + { + ::SetBkColor(hdc, pFont->crBackground); + + *phBackgroundBrush = pFont->hBackground; + fHasBackground = TRUE; + } + else + { + ::SetBkMode(hdc, TRANSPARENT); + *phBackgroundBrush = static_cast(::GetStockObject(NULL_BRUSH)); + fHasBackground = TRUE; + } + } + + return fHasBackground; +} + + +DAPI_(HRESULT) ThemeSetProgressControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwProgressPercentage + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl && THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) + { + DWORD dwCurrentProgress = LOWORD(pControl->dwData); + + if (dwCurrentProgress != dwProgressPercentage) + { + DWORD dwColor = HIWORD(pControl->dwData); + pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor); + + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW) + { + if (!::InvalidateRect(hWnd, NULL, FALSE)) + { + ExitWithLastError(hr, "Failed to invalidate progress bar window."); + } + } + else + { + ::SendMessageW(hWnd, PBM_SETPOS, dwProgressPercentage, 0); + } + + hr = S_OK; + } + else + { + hr = S_FALSE; + } + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeSetProgressControlColor( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwColorIndex + ) +{ + HRESULT hr = S_FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + + // Only set color on owner draw progress bars. + if (pControl && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)) + { + DWORD dwCurrentColor = HIWORD(pControl->dwData); + + if (dwCurrentColor != dwColorIndex) + { + DWORD dwCurrentProgress = LOWORD(pControl->dwData); + pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex); + + if (!::InvalidateRect(hWnd, NULL, FALSE)) + { + ExitWithLastError(hr, "Failed to invalidate progress bar window."); + } + + hr = S_OK; + } + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeSetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __in_z_opt LPCWSTR wzText + ) +{ + return ThemeSetTextControlEx(pTheme, dwControl, FALSE, wzText); +} + + +DAPI_(HRESULT) ThemeSetTextControlEx( + __in const THEME* pTheme, + __in DWORD dwControl, + __in BOOL fUpdate, + __in_z_opt LPCWSTR wzText + ) +{ + HRESULT hr = S_OK; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + if (fUpdate) + { + ::ShowWindow(hWnd, SW_HIDE); + } + + if (!::SetWindowTextW(hWnd, wzText)) + { + ExitWithLastError(hr, "Failed to set control text."); + } + + if (fUpdate) + { + ::ShowWindow(hWnd, SW_SHOW); + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeGetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __out_z LPWSTR* psczText + ) +{ + HRESULT hr = S_OK; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + DWORD cchText = 0; + DWORD cchTextRead = 0; + + // Ensure the string has room for at least one character. + hr = StrMaxLength(*psczText, reinterpret_cast(&cchText)); + ExitOnFailure(hr, "Failed to get text buffer length."); + + if (!cchText) + { + cchText = GROW_WINDOW_TEXT; + + hr = StrAlloc(psczText, cchText); + ExitOnFailure(hr, "Failed to grow text buffer."); + } + + // Read (and keep growing buffer) until we finally read less than there + // is room in the buffer. + for (;;) + { + cchTextRead = ::GetWindowTextW(hWnd, *psczText, cchText); + if (cchTextRead + 1 < cchText) + { + break; + } + else + { + cchText = cchTextRead + GROW_WINDOW_TEXT; + + hr = StrAlloc(psczText, cchText); + ExitOnFailure(hr, "Failed to grow text buffer again."); + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeUpdateCaption( + __in THEME* pTheme, + __in_z LPCWSTR wzCaption + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0); + ExitOnFailure(hr, "Failed to update theme caption."); + +LExit: + return hr; +} + + +DAPI_(void) ThemeSetFocus( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hwndFocus = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hwndFocus && !ThemeControlEnabled(pTheme, dwControl)) + { + hwndFocus = ::GetNextDlgTabItem(pTheme->hwndParent, hwndFocus, FALSE); + } + + if (hwndFocus) + { + ::SetFocus(hwndFocus); + } +} + + +DAPI_(void) ThemeShowChild( + __in THEME* pTheme, + __in THEME_CONTROL* pParentControl, + __in DWORD dwIndex + ) +{ + // show one child, hide the rest + for (DWORD i = 0; i < pParentControl->cControls; ++i) + { + THEME_CONTROL* pControl = pParentControl->rgControls + i; + ShowControl(pTheme, pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL); + } +} + + +// Internal functions. + +static HRESULT RegisterWindowClasses( + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + WNDCLASSW wcHyperlink = { }; + WNDCLASSW wcPanel = { }; + + vhCursorHand = ::LoadCursorA(NULL, IDC_HAND); + + // Base the theme hyperlink class on a button but give it the "hand" icon. + if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink)) + { + ExitWithLastError(hr, "Failed to get button window class."); + } + + wcHyperlink.lpszClassName = THEME_WC_HYPERLINK; +#pragma prefast(push) +#pragma prefast(disable:25068) + wcHyperlink.hCursor = vhCursorHand; +#pragma prefast(pop) + + if (!::RegisterClassW(&wcHyperlink)) + { + ExitWithLastError(hr, "Failed to get button window class."); + } + vhHyperlinkRegisteredModule = hModule; + + // Panel is its own do-nothing class. + wcPanel.lpfnWndProc = PanelWndProc; + wcPanel.hInstance = hModule; + wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW); + wcPanel.lpszClassName = THEME_WC_PANEL; + if (!::RegisterClassW(&wcPanel)) + { + ExitWithLastError(hr, "Failed to register window."); + } + vhPanelRegisteredModule = hModule; + + +LExit: + return hr; +} + +static HRESULT ParseTheme( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMDocument* pixd, + __out THEME** ppTheme + ) +{ + static WORD wThemeId = 0; + + HRESULT hr = S_OK; + THEME* pTheme = NULL; + IXMLDOMElement *pThemeElement = NULL; + + hr = pixd->get_documentElement(&pThemeElement); + ExitOnFailure(hr, "Failed to get theme element."); + + pTheme = static_cast(MemAlloc(sizeof(THEME), TRUE)); + ExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); + + pTheme->wId = ++wThemeId; + + // Parse the optional background resource image. + hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); + ExitOnFailure(hr, "Failed while parsing theme image."); + + // Parse the fonts. + hr = ParseFonts(pThemeElement, pTheme); + ExitOnFailure(hr, "Failed to parse theme fonts."); + + // Parse the window element. + hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme); + ExitOnFailure(hr, "Failed to parse theme window element."); + + *ppTheme = pTheme; + pTheme = NULL; + +LExit: + ReleaseObject(pThemeElement); + + if (pTheme) + { + ThemeFree(pTheme); + } + + return hr; +} + +static HRESULT ParseImage( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HBITMAP* phImage + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + LPWSTR sczImageFile = NULL; + int iResourceId = 0; + Gdiplus::Bitmap* pBitmap = NULL; + + hr = XmlGetAttribute(pElement, L"ImageResource", &bstr); + ExitOnFailure(hr, "Failed to get image resource attribute."); + + if (S_OK == hr) + { + iResourceId = wcstol(bstr, NULL, 10); + + hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(iResourceId), &pBitmap); + // Don't fail. + } + + ReleaseNullBSTR(bstr); + + // Parse the optional background image from a given file. + if (!pBitmap) + { + hr = XmlGetAttribute(pElement, L"ImageFile", &bstr); + ExitOnFailure(hr, "Failed to get image file attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczImageFile); + ExitOnFailure(hr, "Failed to combine image file path."); + } + else + { + hr = PathRelativeToModule(&sczImageFile, bstr, hModule); + ExitOnFailure(hr, "Failed to get image filename."); + } + + hr = GdipBitmapFromFile(sczImageFile, &pBitmap); + // Don't fail. + } + } + + // If there is an image, convert it into a bitmap handle. + if (pBitmap) + { + Gdiplus::Color black; + Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage); + ExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP."); + } + + hr = S_OK; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + ReleaseStr(sczImageFile); + ReleaseBSTR(bstr); + + return hr; +} + + +static HRESULT ParseIcon( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HICON* phIcon + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + LPWSTR sczImageFile = NULL; + int iResourceId = 0; + + hr = XmlGetAttribute(pElement, L"IconResource", &bstr); + ExitOnFailure(hr, "Failed to get icon resource attribute."); + + if (S_OK == hr) + { + iResourceId = wcstol(bstr, NULL, 10); + + *phIcon = reinterpret_cast(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); + ExitOnNullWithLastError(*phIcon, hr, "Failed to load icon."); + } + else + { + ReleaseNullBSTR(bstr); + + hr = XmlGetAttribute(pElement, L"IconFile", &bstr); + ExitOnFailure(hr, "Failed to get icon file attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczImageFile); + ExitOnFailure(hr, "Failed to combine image file path."); + } + else + { + hr = PathRelativeToModule(&sczImageFile, bstr, hModule); + ExitOnFailure(hr, "Failed to get image filename."); + } + + *phIcon = reinterpret_cast(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)); + ExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile); + } + } + +LExit: + ReleaseStr(sczImageFile); + ReleaseBSTR(bstr); + + return hr; +} + + +static HRESULT ParseWindow( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + BSTR bstr = NULL; + LPWSTR sczIconFile = NULL; + + hr = XmlSelectSingleNode(pElement, L"Window", &pixn); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find window element."); + + hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get window AutoResize attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pTheme->nWidth)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find window Width attribute."); + } + ExitOnFailure(hr, "Failed to get window Width attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pTheme->nHeight)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find window Height attribute."); + } + ExitOnFailure(hr, "Failed to get window Height attribute."); + + hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", reinterpret_cast(&pTheme->nMinimumWidth)); + if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); + + hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", reinterpret_cast(&pTheme->nMinimumHeight)); + if (S_FALSE == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); + + hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find window FontId attribute."); + } + ExitOnFailure(hr, "Failed to get window FontId attribute."); + + // Get the optional window icon from a resource. + hr = XmlGetAttribute(pixn, L"IconResource", &bstr); + ExitOnFailure(hr, "Failed to get window IconResource attribute."); + + if (S_OK == hr) + { + pTheme->hIcon = ::LoadIconW(hModule, bstr); + ExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource."); + + ReleaseNullBSTR(bstr); + } + + // Get the optional window icon from a file. + hr = XmlGetAttribute(pixn, L"IconFile", &bstr); + ExitOnFailure(hr, "Failed to get window IconFile attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczIconFile); + ExitOnFailure(hr, "Failed to combine icon file path."); + } + else + { + hr = PathRelativeToModule(&sczIconFile, bstr, hModule); + ExitOnFailure(hr, "Failed to get icon filename."); + } + + pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); + ExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr); + + ReleaseNullBSTR(bstr); + } + + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pTheme->nSourceX)); + if (S_FALSE == hr) + { + pTheme->nSourceX = -1; + } + ExitOnFailure(hr, "Failed to get window SourceX attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pTheme->nSourceY)); + if (S_FALSE == hr) + { + pTheme->nSourceY = -1; + } + ExitOnFailure(hr, "Failed to get window SourceY attribute."); + + // Parse the optional window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle); + ExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute."); + + if (S_FALSE == hr) + { + pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU; + pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED; + } + + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pTheme->uStringId)); + ExitOnFailure(hr, "Failed to get window StringId attribute."); + + if (S_FALSE == hr) + { + pTheme->uStringId = UINT_MAX; + + hr = XmlGetAttribute(pixn, L"Caption", &bstr); + ExitOnFailure(hr, "Failed to get window Caption attribute."); + + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute."); + } + + hr = StrAllocString(&pTheme->sczCaption, bstr, 0); + ExitOnFailure(hr, "Failed to copy window Caption attribute."); + } + + // Parse any image lists. + hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme); + ExitOnFailure(hr, "Failed to parse image lists."); + + // Parse the pages. + hr = ParsePages(hModule, wzRelativePath, pixn, pTheme); + ExitOnFailure(hr, "Failed to parse theme pages."); + + // Parse the non-paged controls. + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL); + ExitOnFailure(hr, "Failed to parse theme controls."); + +LExit: + ReleaseStr(sczIconFile); + ReleaseBSTR(bstr); + ReleaseObject(pixn); + + return hr; +} + + +static HRESULT ParseFonts( + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrName = NULL; + DWORD dwId = 0; + LOGFONTW lf = { }; + COLORREF crForeground = THEME_INVISIBLE_COLORREF; + COLORREF crBackground = THEME_INVISIBLE_COLORREF; + DWORD dwSystemForegroundColor = FALSE; + DWORD dwSystemBackgroundColor = FALSE; + + hr = XmlSelectNodes(pElement, L"Font", &pixnl); + ExitOnFailure(hr, "Failed to find font elements."); + + hr = pixnl->get_length(reinterpret_cast(&pTheme->cFonts)); + ExitOnFailure(hr, "Failed to count the number of theme fonts."); + + if (!pTheme->cFonts) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgFonts = static_cast(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE)); + ExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); + + lf.lfQuality = CLEARTYPE_QUALITY; + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = XmlGetAttributeNumber(pixn, L"Id", &dwId); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find font id."); + + if (pTheme->cFonts <= dwId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Invalid theme font id."); + } + + hr = XmlGetText(pixn, &bstrName); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to get font name."); + + hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), bstrName); + ExitOnFailure(hr, "Failed to copy font name."); + + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&lf.lfHeight)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find font height attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&lf.lfWeight)); + if (S_FALSE == hr) + { + lf.lfWeight = FW_DONTCARE; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to find font weight attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&lf.lfUnderline)); + if (E_NOTFOUND == hr) + { + lf.lfUnderline = FALSE; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to find font underline attribute."); + + hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor); + ExitOnFailure(hr, "Failed to find font foreground color."); + + hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor); + ExitOnFailure(hr, "Failed to find font background color."); + + THEME_FONT* pFont = pTheme->rgFonts + dwId; + if (pFont->hFont) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Theme font id duplicated."); + } + + pFont->hFont = ::CreateFontIndirectW(&lf); + ExitOnNullWithLastError(pFont->hFont, hr, "Failed to create font %u.", dwId); + + pFont->crForeground = crForeground; + if (THEME_INVISIBLE_COLORREF != pFont->crForeground) + { + pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground); + ExitOnNullWithLastError(pFont->hForeground, hr, "Failed to create text foreground brush."); + } + + pFont->crBackground = crBackground; + if (THEME_INVISIBLE_COLORREF != pFont->crBackground) + { + pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground); + ExitOnNullWithLastError(pFont->hBackground, hr, "Failed to create text background brush."); + } + + ReleaseNullBSTR(bstrName); + ReleaseNullObject(pixn); + } + ExitOnFailure(hr, "Failed to enumerate all fonts."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrName); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT GetFontColor( + __in IXMLDOMNode* pixn, + __in_z LPCWSTR wzAttributeName, + __out COLORREF* pColorRef, + __out DWORD* pdwSystemColor + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + + *pdwSystemColor = 0; + + hr = XmlGetAttribute(pixn, wzAttributeName, &bstr); + if (S_FALSE == hr) + { + *pColorRef = THEME_INVISIBLE_COLORREF; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName); + + if (pdwSystemColor) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1)) + { + *pdwSystemColor = COLOR_BTNFACE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1)) + { + *pdwSystemColor = COLOR_BTNTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1)) + { + *pdwSystemColor = COLOR_GRAYTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1)) + { + *pdwSystemColor = COLOR_HIGHLIGHT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1)) + { + *pdwSystemColor = COLOR_HIGHLIGHTTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1)) + { + *pdwSystemColor = COLOR_HOTLIGHT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1)) + { + *pdwSystemColor = COLOR_WINDOW; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1)) + { + *pdwSystemColor = COLOR_WINDOWTEXT; + } + else + { + *pColorRef = wcstoul(bstr, NULL, 16); + } + + if (*pdwSystemColor) + { + *pColorRef = ::GetSysColor(*pdwSystemColor); + } + } + +LExit: + ReleaseBSTR(bstr); + + return hr; +} + +static HRESULT ParsePages( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrType = NULL; + THEME_PAGE* pPage = NULL; + DWORD iPage = 0; + + hr = XmlSelectNodes(pElement, L"Page", &pixnl); + ExitOnFailure(hr, "Failed to find page elements."); + + hr = pixnl->get_length(reinterpret_cast(&pTheme->cPages)); + ExitOnFailure(hr, "Failed to count the number of theme pages."); + + if (!pTheme->cPages) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgPages = static_cast(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE)); + ExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) + { + pPage = pTheme->rgPages + iPage; + + pPage->wId = static_cast(iPage + 1); + + hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying page Name."); + + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage); + ExitOnFailure(hr, "Failed to parse page controls."); + + ++iPage; + + ReleaseNullBSTR(bstrType); + ReleaseNullObject(pixn); + } + ExitOnFailure(hr, "Failed to enumerate all pages."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrType); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT ParseImageLists( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnlImageLists = NULL; + IXMLDOMNode* pixnImageList = NULL; + IXMLDOMNodeList* pixnlImages = NULL; + IXMLDOMNode* pixnImage = NULL; + DWORD dwImageListIndex = 0; + DWORD dwImageCount = 0; + HBITMAP hBitmap = NULL; + BITMAP bm = { }; + BSTR bstr = NULL; + DWORD i = 0; + int iRetVal = 0; + + hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists); + ExitOnFailure(hr, "Failed to find ImageList elements."); + + hr = pixnlImageLists->get_length(reinterpret_cast(&pTheme->cImageLists)); + ExitOnFailure(hr, "Failed to count the number of image lists."); + + if (!pTheme->cImageLists) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgImageLists = static_cast(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE)); + ExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists."); + + while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL))) + { + hr = XmlGetAttribute(pixnImageList, L"Name", &bstr); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find ImageList/@Name attribute."); + + hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0); + ExitOnFailure(hr, "Failed to make copy of ImageList name."); + + hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages); + ExitOnFailure(hr, "Failed to select child Image nodes."); + + hr = pixnlImages->get_length(reinterpret_cast(&dwImageCount)); + ExitOnFailure(hr, "Failed to count the number of images in list."); + + if (0 < dwImageCount) + { + i = 0; + while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL))) + { + if (hBitmap) + { + ::DeleteObject(hBitmap); + hBitmap = NULL; + } + hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap); + ExitOnFailure(hr, "Failed to parse image: %u", i); + + if (0 == i) + { + ::GetObjectW(hBitmap, sizeof(BITMAP), &bm); + + pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0); + ExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list."); + } + + iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL); + if (-1 == iRetVal) + { + ExitWithLastError(hr, "Failed to add image %u to image list.", i); + } + + ++i; + } + } + ++dwImageListIndex; + } + +LExit: + if (hBitmap) + { + ::DeleteObject(hBitmap); + } + ReleaseBSTR(bstr); + ReleaseObject(pixnlImageLists); + ReleaseObject(pixnImageList); + ReleaseObject(pixnlImages); + ReleaseObject(pixnImage); + + return hr; +} + +static void GetControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __out DWORD** ppcControls, + __out THEME_CONTROL*** pprgControls + ) +{ + if (pParentControl) + { + *ppcControls = &pParentControl->cControls; + *pprgControls = &pParentControl->rgControls; + } + else + { + *ppcControls = &pTheme->cControls; + *pprgControls = &pTheme->rgControls; + } +} + +static void GetControls( + __in const THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __out DWORD& cControls, + __out THEME_CONTROL*& rgControls + ) +{ + if (pParentControl) + { + cControls = pParentControl->cControls; + rgControls = pParentControl->rgControls; + } + else + { + cControls = pTheme->cControls; + rgControls = pTheme->rgControls; + } +} + +static HRESULT ParseControls( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_opt THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrType = NULL; + DWORD cNewControls = 0; + DWORD iControl = 0; + DWORD iPageControl = 0; + DWORD* pcControls = NULL; + THEME_CONTROL** prgControls = NULL; + + GetControls(pTheme, pParentControl, &pcControls, &prgControls); + + hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage); + ExitOnFailure(hr, "Failed to parse radio buttons."); + + hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl); + ExitOnFailure(hr, "Failed to find control elements."); + + hr = pixnl->get_length(reinterpret_cast(&cNewControls)); + ExitOnFailure(hr, "Failed to count the number of theme controls."); + + if (!cNewControls) + { + ExitFunction1(hr = S_OK); + } + + hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls); + ExitOnFailure(hr, "Failed to reallocate theme controls."); + + cNewControls += *pcControls; + + if (pPage) + { + iPageControl = pPage->cControlIndices; + pPage->cControlIndices += cNewControls; + } + + iControl = *pcControls; + *pcControls = cNewControls; + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) + { + THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN; + + if (!bstrType) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Null element encountered!"); + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1)) + { + type = THEME_CONTROL_TYPE_BILLBOARD; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1)) + { + type = THEME_CONTROL_TYPE_BUTTON; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1)) + { + type = THEME_CONTROL_TYPE_CHECKBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1)) + { + type = THEME_CONTROL_TYPE_COMBOBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1)) + { + type = THEME_CONTROL_TYPE_COMMANDLINK; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1)) + { + type = THEME_CONTROL_TYPE_EDITBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1)) + { + type = THEME_CONTROL_TYPE_HYPERLINK; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1)) + { + type = THEME_CONTROL_TYPE_HYPERTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1)) + { + type = THEME_CONTROL_TYPE_IMAGE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1)) + { + type = THEME_CONTROL_TYPE_LABEL; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1)) + { + type = THEME_CONTROL_TYPE_LISTVIEW; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1)) + { + type = THEME_CONTROL_TYPE_PANEL; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1)) + { + type = THEME_CONTROL_TYPE_PROGRESSBAR; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1)) + { + type = THEME_CONTROL_TYPE_RICHEDIT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1)) + { + type = THEME_CONTROL_TYPE_STATIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1)) + { + type = THEME_CONTROL_TYPE_TAB; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1)) + { + type = THEME_CONTROL_TYPE_TREEVIEW; + } + + if (THEME_CONTROL_TYPE_UNKNOWN != type) + { + THEME_CONTROL* pControl = *prgControls + iControl; + pControl->type = type; + + // billboard children are always the size of the billboard + BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type; + + hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage); + ExitOnFailure(hr, "Failed to parse control."); + + if (fBillboardSizing) + { + pControl->nX = 0; + pControl->nY = 0; + pControl->nWidth = -0; + pControl->nHeight = 0; + } + + if (pPage) + { + pControl->wPageId = pPage->wId; + ++iPageControl; + } + + ++iControl; + } + + ReleaseNullBSTR(bstrType); + ReleaseNullObject(pixn); + } + ExitOnFailure(hr, "Failed to enumerate all controls."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + + AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls."); + +LExit: + ReleaseBSTR(bstrType); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT ParseControl( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in BOOL fSkipDimensions, + __in_opt THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + DWORD dwValue = 0; + BOOL fValue = FALSE; + BSTR bstrText = NULL; + BOOL fAnyTextChildren = FALSE; + BOOL fAnyNoteChildren = FALSE; + + hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying control Name attribute."); + + hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying control EnableCondition attribute."); + + hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying control VisibleCondition attribute."); + + if (!fSkipDimensions) + { + hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pControl->nX)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find control X attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pControl->nY)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find control Y attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pControl->nHeight)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find control Height attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pControl->nWidth)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ExitOnFailure(hr, "Failed to find control Width attribute."); + } + + // Parse the optional background resource image. + hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage); + ExitOnFailure(hr, "Failed while parsing control image."); + + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pControl->nSourceX)); + if (S_FALSE == hr) + { + pControl->nSourceX = -1; + } + ExitOnFailure(hr, "Failed when querying control SourceX attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pControl->nSourceY)); + if (S_FALSE == hr) + { + pControl->nSourceY = -1; + } + ExitOnFailure(hr, "Failed when querying control SourceY attribute."); + + hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId); + if (S_FALSE == hr) + { + pControl->dwFontId = THEME_INVALID_ID; + } + ExitOnFailure(hr, "Failed when querying control FontId attribute."); + + // Parse the optional window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle); + ExitOnFailure(hr, "Failed when querying control HexStyle attribute."); + + // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above. + hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed when querying control TabStop attribute."); + + if (fValue) + { + pControl->dwStyle |= WS_TABSTOP; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed when querying control Visible attribute."); + + if (fValue) + { + pControl->dwStyle |= WS_VISIBLE; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute."); + + if (fValue) + { + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"DisableAutomaticBehavior", &pControl->fDisableVariableFunctionality); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute."); + } + + hr = ParseActions(pixn, pControl); + ExitOnFailure(hr, "Failed to parse action nodes of the control."); + + hr = ParseText(pixn, pControl, &fAnyTextChildren); + ExitOnFailure(hr, "Failed to parse text nodes of the control."); + + hr = ParseTooltips(pixn, pControl, &fAnyTextChildren); + ExitOnFailure(hr, "Failed to parse control Tooltip."); + + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + hr = ParseNotes(pixn, pControl, &fAnyNoteChildren); + ExitOnFailure(hr, "Failed to parse note text nodes of the control."); + } + + if (fAnyTextChildren || fAnyNoteChildren) + { + pControl->uStringId = UINT_MAX; + } + else + { + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pControl->uStringId)); + ExitOnFailure(hr, "Failed when querying control StringId attribute."); + + if (S_FALSE == hr) + { + pControl->uStringId = UINT_MAX; + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type || THEME_CONTROL_TYPE_PANEL == pControl->type) + { + // Billboards and panels have child elements and we don't want to pick up child element text in the parents. + hr = S_OK; + } + else + { + hr = XmlGetText(pixn, &bstrText); + ExitOnFailure(hr, "Failed to get control inner text."); + + if (S_OK == hr) + { + hr = StrAllocString(&pControl->sczText, bstrText, 0); + ExitOnFailure(hr, "Failed to copy control text."); + + ReleaseNullBSTR(bstrText); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + } + } + } + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute."); + + pControl->wBillboardInterval = 5000; + hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue); + if (S_OK == hr && dwValue) + { + pControl->wBillboardInterval = static_cast(dwValue & 0xFFFF); + } + ExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute."); + + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); + ExitOnFailure(hr, "Failed to parse billboard children."); + } + else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon); + ExitOnFailure(hr, "Failed while parsing control icon."); + } + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute."); + + if (fValue) + { + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE; + } + } + } + else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type) + { + hr = XmlGetAttributeNumber(pixn, L"HoverFontId", &pControl->dwFontHoverId); + if (S_FALSE == hr) + { + pControl->dwFontHoverId = THEME_INVALID_ID; + } + ExitOnFailure(hr, "Failed when querying control HoverFontId attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId); + if (S_FALSE == hr) + { + pControl->dwFontSelectedId = THEME_INVALID_ID; + } + ExitOnFailure(hr, "Failed when querying control SelectedFontId attribute."); + } + else if (THEME_CONTROL_TYPE_LABEL == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= SS_CENTER; + } + ExitOnFailure(hr, "Failed when querying Label/@Center attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= SS_NOPREFIX; + } + ExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute."); + } + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + // Parse the optional extended window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle); + ExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute."); + + hr = XmlGetAttribute(pixn, L"ImageList", &bstrText); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]); + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]); + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]); + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]); + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText); + } + + hr = ParseColumns(pixn, pControl); + ExitOnFailure(hr, "Failed to parse columns."); + } + else if (THEME_CONTROL_TYPE_PANEL == pControl->type) + { + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); + ExitOnFailure(hr, "Failed to parse panel children."); + } + else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type) + { + hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute."); + } + else if (THEME_CONTROL_TYPE_TAB == pControl->type) + { + hr = ParseTabs(pixn, pControl); + ExitOnFailure(hr, "Failed to parse tabs"); + } + else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type) + { + pControl->dwStyle |= TVS_DISABLEDRAGDROP; + + hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle &= ~TVS_DISABLEDRAGDROP; + } + ExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_FULLROWSELECT; + } + ExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_HASBUTTONS; + } + ExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_SHOWSELALWAYS; + } + ExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_LINESATROOT; + } + ExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_HASLINES; + } + ExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute."); + } + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseActions( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrType = NULL; + + hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl); + ExitOnFailure(hr, "Failed to select child action nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cActions)); + ExitOnFailure(hr, "Failed to count the number of action nodes."); + + if (0 < pControl->cActions) + { + MemAllocArray(reinterpret_cast(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions); + ExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType))) + { + if (!bstrType) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Null element encountered!"); + } + + THEME_ACTION* pAction = pControl->rgActions + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY; + + hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName); + ExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE; + + hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName); + ExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute."); + + hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute."); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW; + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType); + } + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType); + } + + if (!pAction->sczCondition) + { + if (pControl->pDefaultAction) + { + hr = E_INVALIDDATA; + ExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName); + } + + pControl->pDefaultAction = pAction; + } + + ++i; + ReleaseNullBSTR(bstrType); + ReleaseObject(pixnChild); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrType); + + return hr; +} + + +static HRESULT ParseColumns( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Column", &pixnl); + ExitOnFailure(hr, "Failed to select child column nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cColumns)); + ExitOnFailure(hr, "Failed to count the number of control columns."); + + if (0 < pControl->cColumns) + { + hr = MemAllocArray(reinterpret_cast(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns); + ExitOnFailure(hr, "Failed to allocate column structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + hr = XmlGetText(pixnChild, &bstrText); + ExitOnFailure(hr, "Failed to get inner text of column element."); + + hr = XmlGetAttributeNumber(pixnChild, L"Width", reinterpret_cast(&pControl->ptcColumns[i].nBaseWidth)); + if (S_FALSE == hr) + { + pControl->ptcColumns[i].nBaseWidth = 100; + } + ExitOnFailure(hr, "Failed to get column width attribute."); + + hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pControl->ptcColumns[i].fExpands)); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get expands attribute."); + + hr = StrAllocString(&(pControl->ptcColumns[i].pszName), bstrText, 0); + ExitOnFailure(hr, "Failed to copy column name."); + + ++i; + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseRadioButtons( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + DWORD cRadioButtons = 0; + DWORD iControl = 0; + DWORD iPageControl = 0; + IXMLDOMNodeList* pixnlRadioButtons = NULL; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnRadioButtons = NULL; + IXMLDOMNode* pixnChild = NULL; + LPWSTR sczName = NULL; + THEME_CONTROL* pControl = NULL; + BOOL fFirst = FALSE; + DWORD* pcControls = NULL; + THEME_CONTROL** prgControls = NULL; + + GetControls(pTheme, pParentControl, &pcControls, &prgControls); + + hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons); + ExitOnFailure(hr, "Failed to select RadioButtons nodes."); + + while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL))) + { + hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying RadioButtons Name."); + + hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl); + ExitOnFailure(hr, "Failed to select RadioButton nodes."); + + hr = pixnl->get_length(reinterpret_cast(&cRadioButtons)); + ExitOnFailure(hr, "Failed to count the number of RadioButton nodes."); + + if (cRadioButtons) + { + if (pPage) + { + iPageControl = pPage->cControlIndices; + pPage->cControlIndices += cRadioButtons; + } + + hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons); + ExitOnFailure(hr, "Failed to reallocate theme controls."); + + iControl = *pcControls; + *pcControls += cRadioButtons; + + fFirst = TRUE; + + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + pControl = *prgControls + iControl; + pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON; + + hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage); + ExitOnFailure(hr, "Failed to parse control."); + + if (fFirst) + { + pControl->dwStyle |= WS_GROUP; + fFirst = FALSE; + } + + hr = StrAllocString(&pControl->sczVariable, sczName, 0); + ExitOnFailure(hr, "Failed to copy radio button variable."); + + if (pPage) + { + pControl->wPageId = pPage->wId; + ++iPageControl; + } + + ++iControl; + } + + if (!fFirst) + { + pControl->fLastRadioButton = TRUE; + } + } + } + +LExit: + ReleaseStr(sczName); + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseObject(pixnlRadioButtons); + ReleaseObject(pixnRadioButtons); + + return hr; +} + + +static HRESULT ParseTabs( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Tab", &pixnl); + ExitOnFailure(hr, "Failed to select child tab nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cTabs)); + ExitOnFailure(hr, "Failed to count the number of tabs."); + + if (0 < pControl->cTabs) + { + hr = MemAllocArray(reinterpret_cast(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs); + ExitOnFailure(hr, "Failed to allocate tab structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + hr = XmlGetText(pixnChild, &bstrText); + ExitOnFailure(hr, "Failed to get inner text of tab element."); + + hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0); + ExitOnFailure(hr, "Failed to copy tab name."); + + ++i; + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseText( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Text", &pixnl); + ExitOnFailure(hr, "Failed to select child Text nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalText)); + ExitOnFailure(hr, "Failed to count the number of Text nodes."); + + *pfAnyChildren |= 0 < pControl->cConditionalText; + + if (0 < pControl->cConditionalText) + { + MemAllocArray(reinterpret_cast(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText); + ExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i; + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying Text/@Condition attribute."); + + hr = XmlGetText(pixnChild, &bstrText); + ExitOnFailure(hr, "Failed to get inner text of Text element."); + + if (S_OK == hr) + { + if (pConditionalText->sczCondition) + { + hr = StrAllocString(&pConditionalText->sczText, bstrText, 0); + ExitOnFailure(hr, "Failed to copy text to conditional text."); + + ++i; + } + else + { + if (pControl->sczText) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName); + } + + hr = StrAllocString(&pControl->sczText, bstrText, 0); + ExitOnFailure(hr, "Failed to copy text to control."); + + // Unconditional text entries aren't stored in the conditional text list. + --pControl->cConditionalText; + } + } + + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseTooltips( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren +) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild); + ExitOnFailure(hr, "Failed to select child Tooltip node."); + + if (S_OK == hr) + { + *pfAnyChildren |= TRUE; + + hr = XmlGetText(pixnChild, &bstrText); + ExitOnFailure(hr, "Failed to get inner text of Tooltip element."); + + if (S_OK == hr) + { + hr = StrAllocString(&pControl->sczTooltip, bstrText, 0); + ExitOnFailure(hr, "Failed to copy tooltip text to control."); + } + } + +LExit: + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseNotes( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __out BOOL* pfAnyChildren + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Note", &pixnl); + ExitOnFailure(hr, "Failed to select child Note nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalNotes)); + ExitOnFailure(hr, "Failed to count the number of Note nodes."); + + if (pfAnyChildren) + { + *pfAnyChildren = 0 < pControl->cConditionalNotes; + } + + if (0 < pControl->cConditionalNotes) + { + MemAllocArray(reinterpret_cast(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes); + ExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + i; + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed when querying Note/@Condition attribute."); + + hr = XmlGetText(pixnChild, &bstrText); + ExitOnFailure(hr, "Failed to get inner text of Note element."); + + if (S_OK == hr) + { + if (pConditionalNote->sczCondition) + { + hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0); + ExitOnFailure(hr, "Failed to copy text to conditional note text."); + + ++i; + } + else + { + if (pControl->sczNote) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName); + } + + hr = StrAllocString(&pControl->sczNote, bstrText, 0); + ExitOnFailure(hr, "Failed to copy text to command link control."); + + // Unconditional note entries aren't stored in the conditional notes list. + --pControl->cConditionalNotes; + } + } + + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT StartBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + // kick off + pControl->dwData = 0; + OnBillboardTimer(pTheme, pTheme->hwndParent, dwControl); + + if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL)) + { + ExitWithLastError(hr, "Failed to start billboard."); + } + + hr = S_OK; + } + } + +LExit: + return hr; +} + + +static HRESULT StopBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + ThemeControlEnable(pTheme, dwControl, FALSE); + + if (::KillTimer(pTheme->hwndParent, pControl->wId)) + { + hr = S_OK; + } + } + } + + return hr; +} + + +static HRESULT FindImageList( + __in THEME* pTheme, + __in_z LPCWSTR wzImageListName, + __out HIMAGELIST *phImageList + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pTheme->cImageLists; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1)) + { + *phImageList = pTheme->rgImageLists[i].hImageList; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +static HRESULT DrawButton( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE); + // "clicked" gets priority + if (ODS_SELECTED & pdis->itemState) + { + nSourceY += pControl->nHeight * 2; + } + // then hover + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) + { + nSourceY += pControl->nHeight; + } + // then focused + else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState) + { + nSourceY += pControl->nHeight * 3; + } + + ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, pControl->nWidth, pControl->nHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + + DrawControlText(pTheme, pdis, pControl, TRUE, FALSE); + + return S_OK; +} + + +static HRESULT DrawHyperlink( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DrawControlText(pTheme, pdis, pControl, FALSE, TRUE); + return S_OK; +} + + +static void DrawControlText( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl, + __in BOOL fCentered, + __in BOOL fDrawFocusRect + ) +{ + WCHAR wzText[256] = { }; + DWORD cchText = 0; + THEME_FONT* pFont = NULL; + HFONT hfPrev = NULL; + + if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText)))) + { + // nothing to do + return; + } + + if (ODS_SELECTED & pdis->itemState) + { + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId); + } + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) + { + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId); + } + else + { + pFont = pTheme->rgFonts + pControl->dwFontId; + } + + hfPrev = SelectFont(pdis->hDC, pFont->hFont); + + ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL); + + if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState)) + { + ::DrawFocusRect(pdis->hDC, &pdis->rcItem); + } + + SelectFont(pdis->hDC, hfPrev); +} + + +static HRESULT DrawImage( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; + DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left; + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + + BLENDFUNCTION bf = { }; + bf.BlendOp = AC_SRC_OVER; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + // Try to draw the image with transparency and if that fails (usually because the image has no + // alpha channel) then draw the image as is. + if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwWidth, dwHeight, bf)) + { + ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwWidth, dwHeight, SRCCOPY); + } + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + return S_OK; +} + + +static HRESULT DrawProgressBar( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DWORD dwProgressColor = HIWORD(pControl->dwData); + DWORD dwProgressPercentage = LOWORD(pControl->dwData); + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; + DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100; + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * pControl->nHeight); + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + // Draw the left side of the progress bar. + ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwHeight, SRCCOPY); + + // Draw the filled side of the progress bar, if there is any. + if (0 < dwCenter) + { + ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwHeight, SRCCOPY); + } + + // Draw the unfilled side of the progress bar, if there is any. + if (dwCenter < static_cast(pdis->rcItem.right - 2)) + { + ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwHeight, SRCCOPY); + } + + // Draw the right side of the progress bar. + ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + return S_OK; +} + + +static BOOL DrawHoverControl( + __in THEME* pTheme, + __in BOOL fHover + ) +{ + BOOL fChangedHover = FALSE; + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, pTheme->hwndHover)); + + // Only hyperlinks and owner-drawn buttons have hover states. + if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || + (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)))) + { + if (fHover) + { + pControl->dwData |= THEME_CONTROL_DATA_HOVER; + } + else + { + pControl->dwData &= ~THEME_CONTROL_DATA_HOVER; + } + + ::InvalidateRect(pControl->hWnd, NULL, FALSE); + fChangedHover = TRUE; + } + + return fChangedHover; +} + + +static void FreePage( + __in THEME_PAGE* pPage + ) +{ + if (pPage) + { + ReleaseStr(pPage->sczName); + + if (pPage->cSavedVariables) + { + for (DWORD i = 0; i < pPage->cSavedVariables; ++i) + { + ReleaseStr(pPage->rgSavedVariables[i].sczValue); + } + } + + ReleaseMem(pPage->rgSavedVariables); + } +} + + +static void FreeImageList( + __in THEME_IMAGELIST* pImageList + ) +{ + if (pImageList) + { + ReleaseStr(pImageList->sczName); + ImageList_Destroy(pImageList->hImageList); + } +} + +static void FreeControl( + __in THEME_CONTROL* pControl + ) +{ + if (pControl) + { + if (::IsWindow(pControl->hWnd)) + { + ::CloseWindow(pControl->hWnd); + pControl->hWnd = NULL; + } + + ReleaseStr(pControl->sczName); + ReleaseStr(pControl->sczText); + ReleaseStr(pControl->sczTooltip); + ReleaseStr(pControl->sczNote); + ReleaseStr(pControl->sczEnableCondition); + ReleaseStr(pControl->sczVisibleCondition); + ReleaseStr(pControl->sczValue); + ReleaseStr(pControl->sczVariable); + + if (pControl->hImage) + { + ::DeleteBitmap(pControl->hImage); + } + + for (DWORD i = 0; i < pControl->cControls; ++i) + { + FreeControl(pControl->rgControls + i); + } + + for (DWORD i = 0; i < pControl->cActions; ++i) + { + FreeAction(&(pControl->rgActions[i])); + } + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + FreeColumn(&(pControl->ptcColumns[i])); + } + + for (DWORD i = 0; i < pControl->cConditionalText; ++i) + { + FreeConditionalText(&(pControl->rgConditionalText[i])); + } + + for (DWORD i = 0; i < pControl->cConditionalNotes; ++i) + { + FreeConditionalText(&(pControl->rgConditionalNotes[i])); + } + + for (DWORD i = 0; i < pControl->cTabs; ++i) + { + FreeTab(&(pControl->pttTabs[i])); + } + + ReleaseMem(pControl->rgActions) + ReleaseMem(pControl->ptcColumns); + ReleaseMem(pControl->rgConditionalText); + ReleaseMem(pControl->rgConditionalNotes); + ReleaseMem(pControl->pttTabs); + } +} + + +static void FreeAction( + __in THEME_ACTION* pAction + ) +{ + switch (pAction->type) + { + case THEME_ACTION_TYPE_BROWSE_DIRECTORY: + ReleaseStr(pAction->BrowseDirectory.sczVariableName); + break; + case THEME_ACTION_TYPE_CHANGE_PAGE: + ReleaseStr(pAction->ChangePage.sczPageName); + break; + } + + ReleaseStr(pAction->sczCondition); +} + + +static void FreeColumn( + __in THEME_COLUMN* pColumn + ) +{ + ReleaseStr(pColumn->pszName); +} + + +static void FreeConditionalText( + __in THEME_CONDITIONAL_TEXT* pConditionalText + ) +{ + ReleaseStr(pConditionalText->sczCondition); + ReleaseStr(pConditionalText->sczText); +} + + +static void FreeTab( + __in THEME_TAB* pTab + ) +{ + ReleaseStr(pTab->pszName); +} + + +static void FreeFont( + __in THEME_FONT* pFont + ) +{ + if (pFont) + { + if (pFont->hBackground) + { + ::DeleteObject(pFont->hBackground); + pFont->hBackground = NULL; + } + + if (pFont->hForeground) + { + ::DeleteObject(pFont->hForeground); + pFont->hForeground = NULL; + } + + if (pFont->hFont) + { + ::DeleteObject(pFont->hFont); + pFont->hFont = NULL; + } + } +} + + +static DWORD CALLBACK RichEditStreamFromFileHandleCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG* pcb + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = reinterpret_cast(dwCookie); + + if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast(pcb), NULL)) + { + ExitWithLastError(hr, "Failed to read file"); + } + +LExit: + return hr; +} + + +static DWORD CALLBACK RichEditStreamFromMemoryCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG* pcb + ) +{ + HRESULT hr = S_OK; + MEMBUFFER_FOR_RICHEDIT* pBuffer = reinterpret_cast(dwCookie); + DWORD cbCopy = 0; + + if (pBuffer->iData < pBuffer->cbData) + { + cbCopy = min(static_cast(cb), pBuffer->cbData - pBuffer->iData); + memcpy(pbBuff, pBuffer->rgbData + pBuffer->iData, cbCopy); + + pBuffer->iData += cbCopy; + Assert(pBuffer->iData <= pBuffer->cbData); + } + + *pcb = cbCopy; + return hr; +} + + +static void CALLBACK OnBillboardTimer( + __in THEME* pTheme, + __in HWND hwnd, + __in UINT_PTR idEvent + ) +{ + HWND hwndControl = ::GetDlgItem(hwnd, static_cast(idEvent)); + if (hwndControl) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hwndControl)); + AssertSz(pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages."); + + if (pControl) + { + if (pControl->dwData < pControl->cControls) + { + ThemeShowChild(pTheme, pControl, pControl->dwData); + } + else if (pControl->fBillboardLoops) + { + pControl->dwData = 0; + ThemeShowChild(pTheme, pControl, pControl->dwData); + } + else // no more looping + { + ::KillTimer(hwnd, idEvent); + } + + ++pControl->dwData; + } + } +} + +static void OnBrowseDirectory( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH] = { }; + BROWSEINFOW browseInfo = { }; + PIDLIST_ABSOLUTE pidl = NULL; + + browseInfo.hwndOwner = hWnd; + browseInfo.pszDisplayName = wzPath; + browseInfo.lpszTitle = pTheme->sczCaption; + browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; + pidl = ::SHBrowseForFolderW(&browseInfo); + if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) + { + // Since editbox changes aren't immediately saved off, we have to treat them differently. + THEME_CONTROL* pTargetControl = NULL; + + for (DWORD i = 0; i < pTheme->cControls; ++i) + { + THEME_CONTROL* pControl = pTheme->rgControls + i; + + if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1)) + { + pTargetControl = pControl; + break; + } + } + + if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality) + { + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); + ExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName); + } + else if (pTheme->pfnSetStringVariable) + { + hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); + } + else if (pTargetControl) + { + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); + ExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName); + } + + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); + } + +LExit: + if (pidl) + { + ::CoTaskMemFree(pidl); + } +} + +static BOOL OnButtonClicked( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + BOOL fHandled = FALSE; + + if (THEME_CONTROL_TYPE_BUTTON == pControl->type || THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + if (pControl->cActions) + { + fHandled = TRUE; + THEME_ACTION* pChosenAction = pControl->pDefaultAction; + + if (pTheme->pfnEvaluateCondition) + { + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. + // This is the current implementation and can change at any time. + for (DWORD j = 0; j < pControl->cActions; ++j) + { + THEME_ACTION* pAction = pControl->rgActions + j; + + if (pAction->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition); + + if (fCondition) + { + pChosenAction = pAction; + break; + } + } + } + } + + if (pChosenAction) + { + switch (pChosenAction->type) + { + case THEME_ACTION_TYPE_BROWSE_DIRECTORY: + OnBrowseDirectory(pTheme, hWnd, pChosenAction); + break; + + case THEME_ACTION_TYPE_CLOSE_WINDOW: + ::SendMessageW(hWnd, WM_CLOSE, 0, 0); + break; + + case THEME_ACTION_TYPE_CHANGE_PAGE: + DWORD dwPageId = 0; + LPCWSTR pPageNames = pChosenAction->ChangePage.sczPageName; + ThemeGetPageIds(pTheme, &pPageNames, &dwPageId, 1); + + if (!dwPageId) + { + ExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName); + } + + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT); + ThemeShowPage(pTheme, dwPageId, SW_SHOW); + break; + } + } + } + } + else if (!pControl->fDisableVariableFunctionality && (pTheme->pfnSetNumericVariable || pTheme->pfnSetStringVariable)) + { + BOOL fRefresh = FALSE; + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_CHECKBOX: + if (pTheme->pfnSetNumericVariable && pControl->sczName && *pControl->sczName) + { + BOOL fChecked = ThemeIsControlChecked(pTheme, pControl->wId); + pTheme->pfnSetNumericVariable(pControl->sczName, fChecked ? 1 : 0, pTheme->pvVariableContext); + fRefresh = TRUE; + } + break; + case THEME_CONTROL_TYPE_RADIOBUTTON: + if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId)) + { + pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, pTheme->pvVariableContext); + fRefresh = TRUE; + } + break; + } + + if (fRefresh) + { + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); + fHandled = TRUE; + } + } + +LExit: + return fHandled; +} + +static HRESULT OnRichEditEnLink( + __in LPARAM lParam, + __in HWND hWndRichEdit, + __in HWND hWnd + ) +{ + HRESULT hr = S_OK; + LPWSTR sczLink = NULL; + ENLINK* link = reinterpret_cast(lParam); + + switch (link->msg) + { + case WM_LBUTTONDOWN: + { + hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2); + ExitOnFailure(hr, "Failed to allocate string for link."); + + TEXTRANGEW tr; + tr.chrg.cpMin = link->chrg.cpMin; + tr.chrg.cpMax = link->chrg.cpMax; + tr.lpstrText = sczLink; + + if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast(&tr))) + { + hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); + ExitOnFailure(hr, "Failed to launch link: %ls", sczLink); + } + + break; + } + + case WM_SETCURSOR: + ::SetCursor(vhCursorHand); + break; + } + +LExit: + ReleaseStr(sczLink); + + return hr; +} + +static BOOL ControlIsType( + __in const THEME* pTheme, + __in DWORD dwControl, + __in const THEME_CONTROL_TYPE type + ) +{ + BOOL fIsType = FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + fIsType = (pControl && type == pControl->type); + } + + return fIsType; +} + +static const THEME_CONTROL* FindControlFromHWnd( + __in const THEME* pTheme, + __in HWND hWnd, + __in_opt const THEME_CONTROL* pParentControl + ) +{ + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + + GetControls(pTheme, pParentControl, cControls, rgControls); + + // As we can't use GWLP_USERDATA (SysLink controls on Windows XP uses it too)... + for (DWORD i = 0; i < cControls; ++i) + { + if (hWnd == rgControls[i].hWnd) + { + return rgControls + i; + } + else if (0 < rgControls[i].cControls) + { + const THEME_CONTROL* pChildControl = FindControlFromHWnd(pTheme, hWnd, rgControls + i); + if (pChildControl) + { + return pChildControl; + } + } + } + + return NULL; +} + +static void GetControlDimensions( + __in const RECT* prcParent, + __in const THEME_CONTROL* pControl, + __out int* piWidth, + __out int* piHeight, + __out int* piX, + __out int* piY + ) +{ + *piWidth = pControl->nWidth < 1 ? pControl->nX < 0 ? prcParent->right + pControl->nWidth : prcParent->right + pControl->nWidth - pControl->nX : pControl->nWidth; + *piHeight = pControl->nHeight < 1 ? pControl->nY < 0 ? prcParent->bottom + pControl->nHeight : prcParent->bottom + pControl->nHeight - pControl->nY : pControl->nHeight; + *piX = pControl->nX < 0 ? prcParent->right + pControl->nX - *piWidth : pControl->nX; + *piY = pControl->nY < 0 ? prcParent->bottom + pControl->nY - *piHeight : pControl->nY; +} + +static HRESULT SizeListViewColumns( + __inout THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + RECT rcParent = { }; + int cNumExpandingColumns = 0; + int iExtraAvailableSize; + + if (!::GetWindowRect(pControl->hWnd, &rcParent)) + { + ExitWithLastError(hr, "Failed to get window rect of listview control."); + } + + iExtraAvailableSize = rcParent.right - rcParent.left; + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + if (pControl->ptcColumns[i].fExpands) + { + ++cNumExpandingColumns; + } + + iExtraAvailableSize -= pControl->ptcColumns[i].nBaseWidth; + } + + // Leave room for a vertical scroll bar just in case. + iExtraAvailableSize -= ::GetSystemMetrics(SM_CXVSCROLL); + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + if (pControl->ptcColumns[i].fExpands) + { + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth + (iExtraAvailableSize / cNumExpandingColumns); + // In case there is any remainder, use it up the first chance we get. + pControl->ptcColumns[i].nWidth += iExtraAvailableSize % cNumExpandingColumns; + iExtraAvailableSize -= iExtraAvailableSize % cNumExpandingColumns; + } + else + { + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth; + } + } + +LExit: + return hr; +} + + +static HRESULT ShowControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId, + __out_opt HWND* phwndFocus + ) +{ + HRESULT hr = S_OK; + DWORD iPageControl = 0; + HWND hwndFocus = NULL; + LPWSTR sczText = NULL; + THEME_SAVEDVARIABLE* pSavedVariable = NULL; + BOOL fHide = SW_HIDE == nCmdShow; + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPageId); + + // Save the editbox value if necessary (other control types save their values immediately). + if (pTheme->pfnSetStringVariable && !pControl->fDisableVariableFunctionality && + fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) + { + hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText); + ExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); + + hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); + } + + HWND hWnd = pControl->hWnd; + + if (fHide && pControl->wPageId) + { + ::ShowWindow(hWnd, SW_HIDE); + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + StopBillboard(pTheme, pControl->wId); + } + + ExitFunction(); + } + + BOOL fEnabled = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); + BOOL fVisible = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDDEN); + + if (!pControl->fDisableVariableFunctionality) + { + if (pTheme->pfnEvaluateCondition) + { + // If the control has a VisibleCondition, check if it's true. + if (pControl->sczVisibleCondition) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + } + + // If the control has an EnableCondition, check if it's true. + if (pControl->sczEnableCondition) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + } + } + + // Try to format each control's text based on context, except for editboxes since their text comes from the user. + if (pTheme->pfnFormatString && ((pControl->sczText && *pControl->sczText) || pControl->cConditionalText) && THEME_CONTROL_TYPE_EDITBOX != pControl->type) + { + LPWSTR wzText = pControl->sczText; + LPWSTR wzNote = pControl->sczNote; + + if (pTheme->pfnEvaluateCondition) + { + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. + // This is the current implementation and can change at any time. + for (DWORD j = 0; j < pControl->cConditionalText; ++j) + { + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + j; + wzText = pConditionalText->sczText; + + if (pConditionalText->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition); + + if (fCondition) + { + wzText = pConditionalText->sczText; + break; + } + } + } + + for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) + { + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + j; + wzNote = pConditionalNote->sczText; + + if (pConditionalNote->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition); + + if (fCondition) + { + wzNote = pConditionalNote->sczText; + break; + } + } + } + } + + if (wzText && *wzText) + { + hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to format string: %ls", wzText); + } + else + { + ReleaseNullStr(sczText); + } + + ThemeSetTextControl(pTheme, pControl->wId, sczText); + + if (wzNote && *wzNote) + { + hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to format note: %ls", wzNote); + } + else + { + ReleaseNullStr(sczText); + } + + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(sczText)); + } + + // If this is a named control, do variable magic. + if (pControl->sczName && *pControl->sczName) + { + // If this is a checkbox control, + // try to set its default state to the state of a matching named variable. + if (pTheme->pfnGetNumericVariable && THEME_CONTROL_TYPE_CHECKBOX == pControl->type) + { + LONGLONG llValue = 0; + hr = pTheme->pfnGetNumericVariable(pControl->sczName, &llValue, pTheme->pvVariableContext); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName); + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczName; + + if (SUCCEEDED(hr)) + { + hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue); + ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + } + + ++iPageControl; + } + + ThemeSendControlMessage(pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); + } + + // If this is an editbox control, + // try to set its default state to the state of a matching named variable. + if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + hr = pTheme->pfnGetStringVariable(pControl->sczName, &sczText, pTheme->pvVariableContext); + if (E_NOTFOUND == hr) + { + ReleaseNullStr(sczText); + } + else + { + ExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczName); + } + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczName; + + if (SUCCEEDED(hr)) + { + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); + ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + } + + ++iPageControl; + } + + ThemeSetTextControl(pTheme, pControl->wId, sczText); + } + } + + // If this is a radio button associated with a variable, + // try to set its default state to the state of the variable. + if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type && pControl->sczVariable && *pControl->sczVariable) + { + hr = pTheme->pfnGetStringVariable(pControl->sczVariable, &sczText, pTheme->pvVariableContext); + if (E_NOTFOUND == hr) + { + ReleaseNullStr(sczText); + } + else + { + ExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable); + } + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczVariable; + + if (SUCCEEDED(hr)) + { + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); + ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable); + } + + ++iPageControl; + } + + Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1)); + } + } + + if (!fVisible || (!fEnabled && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED))) + { + ::ShowWindow(hWnd, SW_HIDE); + } + else + { + ::EnableWindow(hWnd, !fHide && fEnabled); + + if (!hwndFocus && pControl->wPageId && (pControl->dwStyle & WS_TABSTOP)) + { + hwndFocus = hWnd; + } + + ::ShowWindow(hWnd, nCmdShow); + } + + if (0 < pControl->cControls) + { + ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId); + } + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId) + { + if (fEnabled) + { + StartBillboard(pTheme, pControl->wId); + } + else + { + StopBillboard(pTheme, pControl->wId); + } + } + + if (phwndFocus) + { + *phwndFocus = hwndFocus; + } + +LExit: + ReleaseStr(sczText); + + return hr; +} + +static HRESULT ShowControls( + __in THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId + ) +{ + HRESULT hr = S_OK; + HWND hwndFocus = NULL; + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + + GetControls(pTheme, pParentControl, cControls, rgControls); + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + + // Only look at non-page controls and the specified page's controls. + if (!pControl->wPageId || pControl->wPageId == dwPageId) + { + hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus); + ExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i); + } + } + + if (hwndFocus) + { + ::SetFocus(hwndFocus); + } + +LExit: + return hr; +} + + +static LRESULT CALLBACK PanelWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + LRESULT lres = 0; + THEME* pTheme = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); + } + break; + + case WM_NCDESTROY: + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lres; + + case WM_NCHITTEST: + return HTCLIENT; + break; + + case WM_DRAWITEM: + ThemeDrawControl(pTheme, reinterpret_cast(lParam)); + return TRUE; + } + + return ThemeDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); +} + + +static HRESULT LoadControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in HWND hwndParent, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ) +{ + HRESULT hr = S_OK; + RECT rcParent = { }; + LPWSTR sczText = NULL; + BOOL fStartNewGroup = FALSE; + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + + if (!pParentControl) + { + AssertSz(!pTheme->hwndParent, "Theme already loaded controls because it has a parent window."); + pTheme->hwndParent = hwndParent; + } + + GetControls(pTheme, pParentControl, cControls, rgControls); + ::GetClientRect(pParentControl ? pParentControl->hWnd : pTheme->hwndParent, &rcParent); + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; + LPCWSTR wzWindowClass = NULL; + DWORD dwWindowBits = WS_CHILD; + DWORD dwWindowExBits = 0; + + if (fStartNewGroup) + { + dwWindowBits |= WS_GROUP; + fStartNewGroup = FALSE; + } + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_BILLBOARD: + __fallthrough; + case THEME_CONTROL_TYPE_PANEL: + wzWindowClass = THEME_WC_PANEL; + dwWindowBits |= WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + dwWindowExBits |= WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT; +#ifdef DEBUG + StrAllocFormatted(&pControl->sczText, L"Panel '%ls', id: %d", pControl->sczName, pControl->wId); +#endif + break; + + case THEME_CONTROL_TYPE_CHECKBOX: + dwWindowBits |= BS_AUTOCHECKBOX | BS_MULTILINE; // checkboxes are basically buttons with an extra bit tossed in. + __fallthrough; + case THEME_CONTROL_TYPE_BUTTON: + wzWindowClass = WC_BUTTONW; + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + dwWindowBits |= BS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + break; + + case THEME_CONTROL_TYPE_COMBOBOX: + wzWindowClass = WC_COMBOBOXW; + dwWindowBits |= CBS_DROPDOWNLIST | CBS_HASSTRINGS; + break; + + case THEME_CONTROL_TYPE_COMMANDLINK: + wzWindowClass = WC_BUTTONW; + dwWindowBits |= BS_COMMANDLINK; + break; + + case THEME_CONTROL_TYPE_EDITBOX: + wzWindowClass = WC_EDITW; + dwWindowBits |= ES_LEFT | ES_AUTOHSCROLL; + dwWindowExBits = WS_EX_CLIENTEDGE; + break; + + case THEME_CONTROL_TYPE_HYPERLINK: // hyperlinks are basically just owner drawn buttons. + wzWindowClass = THEME_WC_HYPERLINK; + dwWindowBits |= BS_OWNERDRAW | BTNS_NOPREFIX; + break; + + case THEME_CONTROL_TYPE_HYPERTEXT: + wzWindowClass = WC_LINK; + dwWindowBits |= LWS_NOPREFIX; + break; + + case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps). + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + wzWindowClass = WC_STATICW; + dwWindowBits |= SS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Invalid image or image list coordinates."); + } + break; + + case THEME_CONTROL_TYPE_LABEL: + wzWindowClass = WC_STATICW; + break; + + case THEME_CONTROL_TYPE_LISTVIEW: + // If thmutil is handling the image list for this listview, tell Windows not to free it when the control is destroyed. + if (pControl->rghImageList[0] || pControl->rghImageList[1] || pControl->rghImageList[2] || pControl->rghImageList[3]) + { + pControl->dwStyle |= LVS_SHAREIMAGELISTS; + } + wzWindowClass = WC_LISTVIEWW; + break; + + case THEME_CONTROL_TYPE_PROGRESSBAR: + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + wzWindowClass = WC_STATICW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control. + dwWindowBits |= SS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + else + { + wzWindowClass = PROGRESS_CLASSW; + } + break; + + case THEME_CONTROL_TYPE_RADIOBUTTON: + dwWindowBits |= BS_AUTORADIOBUTTON | BS_MULTILINE; + wzWindowClass = WC_BUTTONW; + + if (pControl->fLastRadioButton) + { + fStartNewGroup = TRUE; + } + break; + + case THEME_CONTROL_TYPE_RICHEDIT: + if (!vhModuleRichEd) + { + hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); + ExitOnFailure(hr, "Failed to load Rich Edit control library."); + } + wzWindowClass = RICHEDIT_CLASSW; + dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY; + break; + + case THEME_CONTROL_TYPE_STATIC: + wzWindowClass = WC_STATICW; + dwWindowBits |= SS_ETCHEDHORZ; + break; + + case THEME_CONTROL_TYPE_TAB: + wzWindowClass = WC_TABCONTROLW; + break; + + case THEME_CONTROL_TYPE_TREEVIEW: + wzWindowClass = WC_TREEVIEWW; + break; + } + ExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type); + + // Default control ids to the theme id and its index in the control array, unless there + // is a specific id to assign to a named control. + WORD wControlId = MAKEWORD(i, pTheme->wId); + for (DWORD iAssignControl = 0; pControl->sczName && iAssignControl < cAssignControlIds; ++iAssignControl) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pControl->sczName, -1, rgAssignControlIds[iAssignControl].wzName, -1)) + { + wControlId = rgAssignControlIds[iAssignControl].wId; + break; + } + } + + pControl->wId = wControlId; + + int w, h, x, y; + GetControlDimensions(&rcParent, pControl, &w, &h, &x, &y); + + BOOL fVisible = pControl->dwStyle & WS_VISIBLE; + BOOL fDisabled = pControl->dwStyle & WS_DISABLED; + + // If the control is supposed to be initially visible and it has a VisibleCondition, check if it's true. + if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + + if (!fVisible) + { + pControl->dwStyle &= ~WS_VISIBLE; + } + } + + // Disable controls that aren't visible so their shortcut keys don't trigger. + if (!fVisible) + { + dwWindowBits |= WS_DISABLED; + fDisabled = TRUE; + } + + // If the control is supposed to be initially enabled and it has an EnableCondition, check if it's true. + if (!fDisabled && pControl->sczEnableCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) + { + BOOL fEnable = TRUE; + + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext); + ExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + + fDisabled = !fEnable; + dwWindowBits |= fDisabled ? WS_DISABLED : 0; + } + + // Honor the HideWhenDisabled option. + if ((pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) && fVisible && fDisabled) + { + fVisible = FALSE; + pControl->dwStyle &= ~WS_VISIBLE; + } + + pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pTheme); + ExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); + + if (pControl->sczTooltip) + { + if (!pTheme->hwndTooltip) + { + 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); + } + + if (pTheme->hwndTooltip) + { + TOOLINFOW toolinfo = {}; + toolinfo.cbSize = sizeof(toolinfo); + toolinfo.hwnd = hwndParent; + toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolinfo.uId = reinterpret_cast(pControl->hWnd); + toolinfo.lpszText = pControl->sczTooltip; + ::SendMessageW(pTheme->hwndTooltip, TTM_ADDTOOLW, 0, reinterpret_cast(&toolinfo)); + } + } + + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + if (pControl->sczNote) + { + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(pControl->sczNote)); + } + + if (pControl->hImage) + { + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast(pControl->hImage)); + } + else if (pControl->hIcon) + { + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_ICON, reinterpret_cast(pControl->hIcon)); + } + } + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE) + { + hr = ::SHAutoComplete(pControl->hWnd, SHACF_FILESYS_ONLY); + } + } + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle); + + hr = SizeListViewColumns(pControl); + ExitOnFailure(hr, "Failed to get size of list view columns."); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + LVCOLUMNW lvc = { }; + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + lvc.cx = pControl->ptcColumns[j].nWidth; + lvc.iSubItem = j; + lvc.pszText = pControl->ptcColumns[j].pszName; + lvc.fmt = LVCFMT_LEFT; + lvc.cchTextMax = 4; + + if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc))) + { + ExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j); + } + + // Return value tells us the old image list, we don't care. + if (pControl->rghImageList[0]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_NORMAL), reinterpret_cast(pControl->rghImageList[0])); + } + else if (pControl->rghImageList[1]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_SMALL), reinterpret_cast(pControl->rghImageList[1])); + } + else if (pControl->rghImageList[2]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_STATE), reinterpret_cast(pControl->rghImageList[2])); + } + else if (pControl->rghImageList[3]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_GROUPHEADER), reinterpret_cast(pControl->rghImageList[3])); + } + } + } + else if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) + { + ::SendMessageW(pControl->hWnd, EM_AUTOURLDETECT, static_cast(TRUE), 0); + ::SendMessageW(pControl->hWnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_LINK); + } + else if (THEME_CONTROL_TYPE_TAB == pControl->type) + { + ULONG_PTR hbrBackground = 0; + if (THEME_INVALID_ID != pControl->dwFontId) + { + hbrBackground = reinterpret_cast(pTheme->rgFonts[pControl->dwFontId].hBackground); + } + else + { + hbrBackground = ::GetClassLongPtr(pTheme->hwndParent, GCLP_HBRBACKGROUND); + } + ::SetClassLongPtr(pControl->hWnd, GCLP_HBRBACKGROUND, hbrBackground); + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + TCITEMW tci = { }; + tci.mask = TCIF_TEXT | TCIF_IMAGE; + tci.iImage = -1; + tci.pszText = pControl->pttTabs[j].pszName; + + if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci))) + { + ExitWithLastError(hr, "Failed to insert tab %u into tab control.", j); + } + } + } + + if (pControlFont) + { + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFont->hFont, FALSE); + } + + // Initialize the text on all "application" (non-page) controls, best effort only. + if (pTheme->pfnFormatString && !pControl->wPageId && pControl->sczText && *pControl->sczText) + { + HRESULT hrFormat = pTheme->pfnFormatString(pControl->sczText, &sczText, pTheme->pvVariableContext); + if (SUCCEEDED(hrFormat)) + { + ThemeSetTextControl(pTheme, pControl->wId, sczText); + } + } + + if (pControl->cControls) + { + hr = LoadControls(pTheme, pControl, pControl->hWnd, rgAssignControlIds, cAssignControlIds); + ExitOnFailure(hr, "Failed to load child controls."); + } + } + +LExit: + ReleaseStr(sczText); + + return hr; +} + +static HRESULT LocalizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + hr = LocalizeControl(pControl, pWixLoc); + ExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName); + } + +LExit: + return hr; +} + +static HRESULT LocalizeControl( + __in THEME_CONTROL* pControl, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + LPWSTR sczLocStringId = NULL; + + if (pControl->sczText && *pControl->sczText) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczText); + ExitOnFailure(hr, "Failed to localize control text."); + } + else if (pControl->sczName) + { + LOC_STRING* plocString = NULL; + + hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName); + ExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName); + + hr = LocGetString(pWixLoc, sczLocStringId, &plocString); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName); + + hr = StrAllocString(&pControl->sczText, plocString->wzText, 0); + ExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText); + } + } + + if (pControl->sczTooltip && *pControl->sczTooltip) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip); + ExitOnFailure(hr, "Failed to localize control tooltip text."); + } + + if (pControl->sczNote && *pControl->sczNote) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczNote); + ExitOnFailure(hr, "Failed to localize control note text."); + } + + for (DWORD j = 0; j < pControl->cConditionalText; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText); + ExitOnFailure(hr, "Failed to localize conditional text."); + } + + for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText); + ExitOnFailure(hr, "Failed to localize conditional note."); + } + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName); + ExitOnFailure(hr, "Failed to localize column text."); + } + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName); + ExitOnFailure(hr, "Failed to localize tab text."); + } + + // Localize control's size, location, and text. + if (pControl->sczName) + { + hr = LocGetControl(pWixLoc, pControl->sczName, &pLocControl); + if (E_NOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to localize control."); + + if (LOC_CONTROL_NOT_SET != pLocControl->nX) + { + pControl->nX = pLocControl->nX; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nY) + { + pControl->nY = pLocControl->nY; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nWidth) + { + pControl->nWidth = pLocControl->nWidth; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nHeight) + { + pControl->nHeight = pLocControl->nHeight; + } + + if (pLocControl->wzText && *pLocControl->wzText) + { + hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0); + ExitOnFailure(hr, "Failed to localize control text."); + } + } + + hr = LocalizeControls(pControl->cControls, pControl->rgControls, pWixLoc); + +LExit: + ReleaseStr(sczLocStringId); + + return hr; +} + +static HRESULT LoadControlsString( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + hr = LoadControlString(pControl, hResModule); + ExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName); + } + +LExit: + return hr; +} + +static HRESULT LoadControlString( + __in THEME_CONTROL* pControl, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + if (UINT_MAX != pControl->uStringId) + { + hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText); + ExitOnFailure(hr, "Failed to load control text."); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + if (UINT_MAX != pControl->ptcColumns[j].uStringId) + { + hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName); + ExitOnFailure(hr, "Failed to load column text."); + } + } + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + if (UINT_MAX != pControl->pttTabs[j].uStringId) + { + hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName); + ExitOnFailure(hr, "Failed to load tab text."); + } + } + } + + hr = LoadControlsString(pControl->cControls, pControl->rgControls, hResModule); + +LExit: + return hr; +} + +static void ResizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const RECT* prcParent + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + ResizeControl(pControl, prcParent); + } +} + +static void ResizeControl( + __in THEME_CONTROL* pControl, + __in const RECT* prcParent + ) +{ + int w, h, x, y; + + GetControlDimensions(prcParent, pControl, &w, &h, &x, &y); + ::MoveWindow(pControl->hWnd, x, y, w, h, TRUE); + +#ifdef DEBUG + if (THEME_CONTROL_TYPE_BUTTON == pControl->type) + { + Trace(REPORT_STANDARD, "Resizing button (%ls/%ls) to (%d,%d)+(%d,%d) for parent (%d,%d)-(%d,%d)", + pControl->sczName, pControl->sczText, x, y, w, h, prcParent->left, prcParent->top, prcParent->right, prcParent->bottom); + } +#endif + + if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + SizeListViewColumns(pControl); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + if (-1 == ::SendMessageW(pControl->hWnd, LVM_SETCOLUMNWIDTH, (WPARAM) (int) (j), (LPARAM) (pControl->ptcColumns[j].nWidth))) + { + Trace(REPORT_DEBUG, "Failed to resize listview column %u with error %u", j, ::GetLastError()); + return; + } + } + } + + if (pControl->cControls) + { + RECT rcControl = { }; + ::GetClientRect(pControl->hWnd, &rcControl); + ResizeControls(pControl->cControls, pControl->rgControls, &rcControl); + } +} + +static void UnloadControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + pControl->hWnd = NULL; + + UnloadControls(pControl->cControls, pControl->rgControls); + } +} + diff --git a/src/dutil/timeutil.cpp b/src/dutil/timeutil.cpp new file mode 100644 index 00000000..dacb2660 --- /dev/null +++ b/src/dutil/timeutil.cpp @@ -0,0 +1,370 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; +const 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" }; +enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone }; +enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone }; + +// prototypes +static HRESULT DayFromString( + __in_z LPCWSTR wzDay, + __out WORD* pwDayOfWeek + ); +static HRESULT MonthFromString( + __in_z LPCWSTR wzMonth, + __out WORD* pwMonthOfYear + ); + + +/******************************************************************** + TimeFromString - converts string to FILETIME + +*******************************************************************/ +extern "C" HRESULT DAPI TimeFromString( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ) +{ + Assert(wzTime && pFileTime); + + HRESULT hr = S_OK; + LPWSTR pwzTime = NULL; + + SYSTEMTIME sysTime = { }; + TIME_PARSER timeParser = DayOfWeek; + + LPCWSTR pwzStart = NULL; + LPWSTR pwzEnd = NULL; + + hr = StrAllocString(&pwzTime, wzTime, 0); + ExitOnFailure(hr, "Failed to copy time."); + + pwzStart = pwzEnd = pwzTime; + while (pwzEnd && *pwzEnd) + { + if (L',' == *pwzEnd || L' ' == *pwzEnd || L':' == *pwzEnd) + { + *pwzEnd = L'\0'; // null terminate + ++pwzEnd; + + while (L' ' == *pwzEnd) + { + ++pwzEnd; // and skip past the blank space + } + + switch (timeParser) + { + case DayOfWeek: + hr = DayFromString(pwzStart, &sysTime.wDayOfWeek); + ExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart); + break; + + case DayOfMonth: + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case MonthOfYear: + hr = MonthFromString(pwzStart, &sysTime.wMonth); + ExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart); + break; + + case Year: + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Hours: + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Minutes: + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Seconds: + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case TimeZone: + // TODO: do something with this in the future, but this should only hit outside of the while loop. + break; + + default: + break; + } + + pwzStart = pwzEnd; + timeParser = (TIME_PARSER)((int)timeParser + 1); + } + + ++pwzEnd; + } + + + if (!::SystemTimeToFileTime(&sysTime, pFileTime)) + { + ExitWithLastError(hr, "Failed to convert system time to file time."); + } + +LExit: + ReleaseStr(pwzTime); + + return hr; +} + +/******************************************************************** + TimeFromString3339 - converts string formated in accorance with RFC3339 to FILETIME + http://tools.ietf.org/html/rfc3339 +*******************************************************************/ +extern "C" HRESULT DAPI TimeFromString3339( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ) +{ + Assert(wzTime && pFileTime); + + HRESULT hr = S_OK; + LPWSTR pwzTime = NULL; + + SYSTEMTIME sysTime = { }; + TIME_PARSERRFC3339 timeParser = RFC3339_Year; + + LPCWSTR pwzStart = NULL; + LPWSTR pwzEnd = NULL; + + hr = StrAllocString(&pwzTime, wzTime, 0); + ExitOnFailure(hr, "Failed to copy time."); + + pwzStart = pwzEnd = pwzTime; + while (pwzEnd && *pwzEnd) + { + if (L'T' == *pwzEnd || L':' == *pwzEnd || L'-' == *pwzEnd) + { + *pwzEnd = L'\0'; // null terminate + ++pwzEnd; + + switch (timeParser) + { + case RFC3339_Year: + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Month: + sysTime.wMonth = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Day: + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Hours: + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Minutes: + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Seconds: + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_TimeZone: + // TODO: do something with this in the future, but this should only hit outside of the while loop. + break; + + default: + break; + } + + pwzStart = pwzEnd; + timeParser = (TIME_PARSERRFC3339)((int)timeParser + 1); + } + + ++pwzEnd; + } + + + if (!::SystemTimeToFileTime(&sysTime, pFileTime)) + { + ExitWithLastError(hr, "Failed to convert system time to file time."); + } + +LExit: + ReleaseStr(pwzTime); + + return hr; +} +/**************************************************************************** +TimeCurrentTime - gets the current time in string format + +****************************************************************************/ +extern "C" HRESULT DAPI TimeCurrentTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ) +{ + SYSTEMTIME st; + + if (fGMT) + { + ::GetSystemTime(&st); + } + else + { + SYSTEMTIME stGMT; + TIME_ZONE_INFORMATION tzi; + + ::GetTimeZoneInformation(&tzi); + ::GetSystemTime(&stGMT); + ::SystemTimeToTzSpecificLocalTime(&tzi, &stGMT, &st); + } + + return StrAllocFormatted(ppwz, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); +} + + +/**************************************************************************** +TimeCurrentDateTime - gets the current date and time in string format, + per format described in RFC 3339 +****************************************************************************/ +extern "C" HRESULT DAPI TimeCurrentDateTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ) +{ + SYSTEMTIME st; + + ::GetSystemTime(&st); + + return TimeSystemDateTime(ppwz, &st, fGMT); +} + + +/**************************************************************************** +TimeSystemDateTime - converts the provided system time struct to string format, + per format described in RFC 3339 +****************************************************************************/ +extern "C" HRESULT DAPI TimeSystemDateTime( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in BOOL fGMT + ) +{ + DWORD dwAbsBias = 0; + + if (fGMT) + { + return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02huZ", pst->wYear, pst->wMonth, pst->wDay, pst->wHour, pst->wMinute, pst->wSecond); + } + else + { + SYSTEMTIME st; + TIME_ZONE_INFORMATION tzi; + + ::GetTimeZoneInformation(&tzi); + ::SystemTimeToTzSpecificLocalTime(&tzi, pst, &st); + dwAbsBias = abs(tzi.Bias); + + 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); + } +} + + +/**************************************************************************** +TimeSystemToDateTimeString - converts the provided system time struct to + string format representing date and time for the specified locale +****************************************************************************/ +HRESULT DAPI TimeSystemToDateTimeString( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME* pst, + __in LCID locale + ) +{ + HRESULT hr = S_OK; + const WCHAR * DATE_FORMAT = L"MMM dd',' yyyy',' "; + const WCHAR * TIME_FORMAT = L"hh':'mm':'ss tt"; + int iLenDate = 0; + int iLenTime = 0; + + iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0); + if (0 >= iLenDate) + { + ExitWithLastError(hr, "Failed to get date format with NULL"); + } + + iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0); + if (0 >= iLenTime) + { + ExitWithLastError(hr, "Failed to get time format with NULL"); + } + + // Between both lengths we account for 2 null terminators, and only need one, so we subtract one + hr = StrAlloc(ppwz, iLenDate + iLenTime - 1); + ExitOnFailure(hr, "Failed to allocate string"); + + if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate)) + { + ExitWithLastError(hr, "Failed to get date format with buffer"); + } + // Space to separate them + (*ppwz)[iLenDate - 1] = ' '; + + if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime)) + { + ExitWithLastError(hr, "Failed to get time format with buffer"); + } + +LExit: + return hr; +} + +/******************************************************************** + DayFromString - converts string to day + +*******************************************************************/ +static HRESULT DayFromString( + __in_z LPCWSTR wzDay, + __out WORD* pwDayOfWeek + ) +{ + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name + + for (WORD i = 0; i < countof(DAY_OF_WEEK); ++i) + { + if (0 == lstrcmpW(wzDay, DAY_OF_WEEK[i])) + { + *pwDayOfWeek = i; + hr = S_OK; + break; + } + } + + return hr; +} + + +/******************************************************************** + MonthFromString - converts string to month + +*******************************************************************/ +static HRESULT MonthFromString( + __in_z LPCWSTR wzMonth, + __out WORD* pwMonthOfYear + ) +{ + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name + + for (WORD i = 0; i < countof(MONTH_OF_YEAR); ++i) + { + if (0 == lstrcmpW(wzMonth, MONTH_OF_YEAR[i])) + { + *pwMonthOfYear = i; + hr = S_OK; + break; + } + } + + return hr; +} diff --git a/src/dutil/uncutil.cpp b/src/dutil/uncutil.cpp new file mode 100644 index 00000000..6deb43bd --- /dev/null +++ b/src/dutil/uncutil.cpp @@ -0,0 +1,54 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +DAPI_(HRESULT) UncConvertFromMountedDrive( + __inout LPWSTR *psczUNCPath, + __in LPCWSTR sczMountedDrivePath + ) +{ + HRESULT hr = S_OK; + DWORD dwLength = 0; + DWORD er = ERROR_SUCCESS; + LPWSTR sczDrive = NULL; + + // Only copy drive letter and colon + hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2); + ExitOnFailure(hr, "Failed to copy drive"); + + // ERROR_NOT_CONNECTED means it's not a mapped drive + er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength); + if (ERROR_MORE_DATA == er) + { + er = ERROR_SUCCESS; + + hr = StrAlloc(psczUNCPath, dwLength); + ExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength); + + er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength); + if (ERROR_CONNECTION_UNAVAIL == er) + { + // 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 + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive); + + // Skip drive letter and colon + hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0); + ExitOnFailure(hr, "Failed to copy rest of database path"); + } + else + { + if (ERROR_SUCCESS == er) + { + er = ERROR_NO_DATA; + } + + ExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive); + } + +LExit: + ReleaseStr(sczDrive); + + return hr; +} diff --git a/src/dutil/uriutil.cpp b/src/dutil/uriutil.cpp new file mode 100644 index 00000000..fc192b3f --- /dev/null +++ b/src/dutil/uriutil.cpp @@ -0,0 +1,538 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// +// UriCanonicalize - canonicalizes a URI. +// +extern "C" HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ) +{ + HRESULT hr = S_OK; + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; + DWORD cch = countof(wz); + + if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE)) + { + ExitWithLastError(hr, "Failed to canonicalize URI."); + } + + hr = StrAllocString(psczUri, wz, cch); + ExitOnFailure(hr, "Failed copy canonicalized URI."); + +LExit: + return hr; +} + + +// +// UriCrack - cracks a URI into constituent parts. +// +extern "C" HRESULT DAPI UriCrack( + __in_z LPCWSTR wzUri, + __out_opt INTERNET_SCHEME* pScheme, + __deref_opt_out_z LPWSTR* psczHostName, + __out_opt INTERNET_PORT* pPort, + __deref_opt_out_z LPWSTR* psczUser, + __deref_opt_out_z LPWSTR* psczPassword, + __deref_opt_out_z LPWSTR* psczPath, + __deref_opt_out_z LPWSTR* psczQueryString + ) +{ + HRESULT hr = S_OK; + URL_COMPONENTSW components = { }; + WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; + WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1]; + WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1]; + WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1]; + + components.dwStructSize = sizeof(URL_COMPONENTSW); + + if (psczHostName) + { + components.lpszHostName = wzHostName; + components.dwHostNameLength = countof(wzHostName); + } + + if (psczUser) + { + components.lpszUserName = wzUserName; + components.dwUserNameLength = countof(wzUserName); + } + + if (psczPassword) + { + components.lpszPassword = wzPassword; + components.dwPasswordLength = countof(wzPassword); + } + + if (psczPath) + { + components.lpszUrlPath = wzPath; + components.dwUrlPathLength = countof(wzPath); + } + + if (psczQueryString) + { + components.lpszExtraInfo = wzQueryString; + components.dwExtraInfoLength = countof(wzQueryString); + } + + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components)) + { + ExitWithLastError(hr, "Failed to crack URI."); + } + + if (pScheme) + { + *pScheme = components.nScheme; + } + + if (psczHostName) + { + hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength); + ExitOnFailure(hr, "Failed to copy host name."); + } + + if (pPort) + { + *pPort = components.nPort; + } + + if (psczUser) + { + hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength); + ExitOnFailure(hr, "Failed to copy user name."); + } + + if (psczPassword) + { + hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength); + ExitOnFailure(hr, "Failed to copy password."); + } + + if (psczPath) + { + hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength); + ExitOnFailure(hr, "Failed to copy path."); + } + + if (psczQueryString) + { + hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength); + ExitOnFailure(hr, "Failed to copy query string."); + } + +LExit: + return hr; +} + + +// +// UriCrackEx - cracks a URI into URI_INFO. +// +extern "C" HRESULT DAPI UriCrackEx( + __in_z LPCWSTR wzUri, + __in URI_INFO* pUriInfo + ) +{ + HRESULT hr = S_OK; + + hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString); + ExitOnFailure(hr, "Failed to crack URI."); + +LExit: + return hr; +} + + +// +// UriInfoUninitialize - frees the memory in a URI_INFO struct. +// +extern "C" void DAPI UriInfoUninitialize( + __in URI_INFO* pUriInfo + ) +{ + ReleaseStr(pUriInfo->sczHostName); + ReleaseStr(pUriInfo->sczUser); + ReleaseStr(pUriInfo->sczPassword); + ReleaseStr(pUriInfo->sczPath); + ReleaseStr(pUriInfo->sczQueryString); + memset(pUriInfo, 0, sizeof(URI_INFO)); +} + + +// +// UriCreate - creates a URI from constituent parts. +// +extern "C" HRESULT DAPI UriCreate( + __inout_z LPWSTR* psczUri, + __in INTERNET_SCHEME scheme, + __in_z_opt LPWSTR wzHostName, + __in INTERNET_PORT port, + __in_z_opt LPWSTR wzUser, + __in_z_opt LPWSTR wzPassword, + __in_z_opt LPWSTR wzPath, + __in_z_opt LPWSTR wzQueryString + ) +{ + HRESULT hr = S_OK; + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; + DWORD cch = countof(wz); + URL_COMPONENTSW components = { }; + + components.dwStructSize = sizeof(URL_COMPONENTSW); + components.nScheme = scheme; + components.lpszHostName = wzHostName; + components.nPort = port; + components.lpszUserName = wzUser; + components.lpszPassword = wzPassword; + components.lpszUrlPath = wzPath; + components.lpszExtraInfo = wzQueryString; + + if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch)) + { + ExitWithLastError(hr, "Failed to create URI."); + } + + hr = StrAllocString(psczUri, wz, cch); + ExitOnFailure(hr, "Failed copy created URI."); + +LExit: + return hr; +} + + +// +// UriGetServerAndResource - gets the server and resource as independent strings from a URI. +// +// NOTE: This function is useful for the InternetConnect/HttpRequest APIs. +// +extern "C" HRESULT DAPI UriGetServerAndResource( + __in_z LPCWSTR wzUri, + __out_z LPWSTR* psczServer, + __out_z LPWSTR* psczResource + ) +{ + HRESULT hr = S_OK; + INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN; + LPWSTR sczHostName = NULL; + INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER; + LPWSTR sczUser = NULL; + LPWSTR sczPassword = NULL; + LPWSTR sczPath = NULL; + LPWSTR sczQueryString = NULL; + + hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString); + ExitOnFailure(hr, "Failed to crack URI."); + + hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL); + ExitOnFailure(hr, "Failed to allocate server URI."); + + hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString); + ExitOnFailure(hr, "Failed to allocate resource URI."); + +LExit: + ReleaseStr(sczQueryString); + ReleaseStr(sczPath); + ReleaseStr(sczPassword); + ReleaseStr(sczUser); + ReleaseStr(sczHostName); + + return hr; +} + + +// +// UriFile - returns the file part of the URI. +// +extern "C" HRESULT DAPI UriFile( + __deref_out_z LPWSTR* psczFile, + __in_z LPCWSTR wzUri + ) +{ + HRESULT hr = S_OK; + WCHAR wz[MAX_PATH + 1]; + DWORD cch = countof(wz); + URL_COMPONENTSW uc = { }; + + uc.dwStructSize = sizeof(uc); + uc.lpszUrlPath = wz; + uc.dwUrlPathLength = cch; + + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc)) + { + ExitWithLastError(hr, "Failed to crack URI."); + } + + // Copy only the file name. Fortunately, PathFile() understands that + // forward slashes can be directory separators like backslashes. + hr = StrAllocString(psczFile, PathFile(wz), 0); + ExitOnFailure(hr, "Failed to copy file name"); + +LExit: + return hr; +} + + +/******************************************************************* + UriProtocol - determines the protocol of a URI. + +********************************************************************/ +extern "C" HRESULT DAPI UriProtocol( + __in_z LPCWSTR wzUri, + __out URI_PROTOCOL* pProtocol + ) +{ + Assert(wzUri && *wzUri); + Assert(pProtocol); + + HRESULT hr = S_OK; + + if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L't' == wzUri[2] || L'T' == wzUri[2]) && + (L'p' == wzUri[3] || L'P' == wzUri[3]) && + (L's' == wzUri[4] || L'S' == wzUri[4]) && + L':' == wzUri[5] && + L'/' == wzUri[6] && + L'/' == wzUri[7]) + { + *pProtocol = URI_PROTOCOL_HTTPS; + } + else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L't' == wzUri[2] || L'T' == wzUri[2]) && + (L'p' == wzUri[3] || L'P' == wzUri[3]) && + L':' == wzUri[4] && + L'/' == wzUri[5] && + L'/' == wzUri[6]) + { + *pProtocol = URI_PROTOCOL_HTTP; + } + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L'p' == wzUri[2] || L'P' == wzUri[2]) && + L':' == wzUri[3] && + L'/' == wzUri[4] && + L'/' == wzUri[5]) + { + *pProtocol = URI_PROTOCOL_FTP; + } + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && + (L'i' == wzUri[1] || L'I' == wzUri[1]) && + (L'l' == wzUri[2] || L'L' == wzUri[2]) && + (L'e' == wzUri[3] || L'E' == wzUri[3]) && + L':' == wzUri[4] && + L'/' == wzUri[5] && + L'/' == wzUri[6]) + { + *pProtocol = URI_PROTOCOL_FILE; + } + else + { + *pProtocol = URI_PROTOCOL_UNKNOWN; + } + + return hr; +} + + +/******************************************************************* + UriRoot - returns the root of the path specified in the URI. + + examples: + file:///C:\path\path -> C:\ + file://server/share/path/path -> \\server\share + http://www.example.com/path/path -> http://www.example.com/ + ftp://ftp.example.com/path/path -> ftp://www.example.com/ + + NOTE: This function should only be used on cannonicalized URIs. + It does not cannonicalize itself. +********************************************************************/ +extern "C" HRESULT DAPI UriRoot( + __in_z LPCWSTR wzUri, + __out LPWSTR* ppwzRoot, + __out_opt URI_PROTOCOL* pProtocol + ) +{ + Assert(wzUri && *wzUri); + Assert(ppwzRoot); + + HRESULT hr = S_OK; + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; + LPCWSTR pwcSlash = NULL; + + hr = UriProtocol(wzUri, &protocol); + ExitOnFailure(hr, "Invalid URI."); + + switch (protocol) + { + case URI_PROTOCOL_FILE: + if (L'/' == wzUri[7]) // file path + { + if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9]) + { + hr = StrAlloc(ppwzRoot, 4); + ExitOnFailure(hr, "Failed to allocate string for root of URI."); + *ppwzRoot[0] = wzUri[8]; + *ppwzRoot[1] = L':'; + *ppwzRoot[2] = L'\\'; + *ppwzRoot[3] = L'\0'; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid file path in URI."); + } + } + else // UNC share + { + pwcSlash = wcschr(wzUri + 8, L'/'); + if (!pwcSlash) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid server name in URI."); + } + else + { + hr = StrAllocString(ppwzRoot, L"\\\\", 64); + ExitOnFailure(hr, "Failed to allocate string for root of URI."); + + pwcSlash = wcschr(pwcSlash + 1, L'/'); + if (pwcSlash) + { + hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8); + ExitOnFailure(hr, "Failed to add server/share to root of URI."); + } + else + { + hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0); + ExitOnFailure(hr, "Failed to add server/share to root of URI."); + } + + // replace all slashes with backslashes to be truly UNC. + for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc) + { + if (L'/' == *pwc) + { + *pwc = L'\\'; + } + } + } + } + break; + + case URI_PROTOCOL_FTP: + pwcSlash = wcschr(wzUri + 6, L'/'); + if (pwcSlash) + { + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); + ExitOnFailure(hr, "Failed allocate root from URI."); + } + else + { + hr = StrAllocString(ppwzRoot, wzUri, 0); + ExitOnFailure(hr, "Failed allocate root from URI."); + } + break; + + case URI_PROTOCOL_HTTP: + pwcSlash = wcschr(wzUri + 7, L'/'); + if (pwcSlash) + { + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); + ExitOnFailure(hr, "Failed allocate root from URI."); + } + else + { + hr = StrAllocString(ppwzRoot, wzUri, 0); + ExitOnFailure(hr, "Failed allocate root from URI."); + } + break; + + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Unknown URI protocol."); + } + + if (pProtocol) + { + *pProtocol = protocol; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI UriResolve( + __in_z LPCWSTR wzUri, + __in_opt LPCWSTR wzBaseUri, + __out LPWSTR* ppwzResolvedUri, + __out_opt const URI_PROTOCOL* pResolvedProtocol + ) +{ + UNREFERENCED_PARAMETER(wzUri); + UNREFERENCED_PARAMETER(wzBaseUri); + UNREFERENCED_PARAMETER(ppwzResolvedUri); + UNREFERENCED_PARAMETER(pResolvedProtocol); + + HRESULT hr = E_NOTIMPL; +#if 0 + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; + + hr = UriProtocol(wzUri, &protocol); + ExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); + + ExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); + + if (URI_PROTOCOL_UNKNOWN == protocol) + { + ExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); + + if (L'/' == *wzUri || L'\\' == *wzUri) + { + hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol); + ExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); + + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); + ExitOnFailure(hr, "Failed to concat file to base URI."); + } + else + { + hr = UriProtocol(wzBaseUri, &protocol); + ExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); + + LPCWSTR pwcFile = const_cast (UriFile(wzBaseUri)); + if (!pwcFile) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); + } + + hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri); + ExitOnFailure(hr, "Failed to allocate string for resolved URI."); + + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); + ExitOnFailure(hr, "Failed to concat file to resolved URI."); + } + } + else + { + hr = StrAllocString(ppwzResolvedUri, wzUri, 0); + ExitOnFailure(hr, "Failed to copy resolved URI."); + } + + if (pResolvedProtocol) + { + *pResolvedProtocol = protocol; + } + +LExit: +#endif + return hr; +} diff --git a/src/dutil/userutil.cpp b/src/dutil/userutil.cpp new file mode 100644 index 00000000..2e77f1df --- /dev/null +++ b/src/dutil/userutil.cpp @@ -0,0 +1,285 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static BOOL CheckIsMemberHelper( + __in_z LPCWSTR pwzGroupUserDomain, + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, + __in DWORD cguiGroupData + ); + +/******************************************************************* + UserBuildDomainUserName - builds a DOMAIN\USERNAME string + +********************************************************************/ +extern "C" HRESULT DAPI UserBuildDomainUserName( + __out_ecount_z(cchDest) LPWSTR wzDest, + __in int cchDest, + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain + ) +{ + HRESULT hr = S_OK; + DWORD cchLeft = cchDest; + WCHAR* pwz = wzDest; + DWORD cchWz = cchDest; + DWORD cch; + + cch = lstrlenW(pwzDomain); + if (cch >= cchLeft) + { + hr = ERROR_MORE_DATA; + ExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain); + } + else if (cch > 0) + { + // handle the domain case + + hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0' + ExitOnFailure(hr, "Failed to copy Domain onto string."); + + cchLeft -= cch; + pwz += cch; + cchWz -= cch; + + if (1 >= cchLeft) + { + hr = ERROR_MORE_DATA; + ExitOnFailure(hr, "Insufficient buffer size while building domain user name"); + } + + hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0' + ExitOnFailure(hr, "Failed to copy backslash onto string."); + + --cchLeft; + ++pwz; + --cchWz; + } + + cch = lstrlenW(pwzName); + if (cch >= cchLeft) + { + hr = ERROR_MORE_DATA; + ExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName); + } + + hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0' + ExitOnFailure(hr, "Failed to copy User name onto string."); + +LExit: + return hr; +} + + +/******************************************************************* + Checks whether a user is a member of a group - outputs the result via lpfMember +********************************************************************/ +extern "C" HRESULT DAPI UserCheckIsMember( + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain, + __in_z LPCWSTR pwzGroupName, + __in_z LPCWSTR pwzGroupDomain, + __out LPBOOL lpfMember + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + DWORD dwRead = 0; + DWORD dwTotal = 0; + LPCWSTR wz = NULL; + GROUP_USERS_INFO_0 *pguiGroupData = NULL; + WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME + WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME + BSTR bstrUser = NULL; + BSTR bstrGroup = NULL; + + IADsGroup *pGroup = NULL; + VARIANT_BOOL vtBoolResult = VARIANT_FALSE; + + hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain); + ExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + + hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain); + ExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + + if (pwzDomain && *pwzDomain) + { + wz = pwzDomain; + } + + er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); + // Ignore these errors, and just go to the fallback checks + if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er) + { + 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)); + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName); + + if (dwRead != dwTotal) + { + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + ExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName); + } + + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + if (NULL != pguiGroupData) + { + ::NetApiBufferFree(pguiGroupData); + pguiGroupData = NULL; + } + + // If we fail with the global groups, try again with the local groups + er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); + // Ignore these errors, and just go to the fallback checks + if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er) + { + 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)); + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName); + + if (dwRead != dwTotal) + { + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + ExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName); + } + + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + // If the above methods failed, let's try active directory + hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser); + ExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain); + + hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup); + ExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain); + + if (lstrlenW(pwzGroupDomain) > 0) + { + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); + ExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->IsMember(bstrUser, &vtBoolResult); + ExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + } + + if (vtBoolResult) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); + ExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->IsMember(bstrUser, &vtBoolResult); + ExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + + if (vtBoolResult) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + +LExit: + ReleaseObject(pGroup); + ReleaseBSTR(bstrUser); + ReleaseBSTR(bstrGroup); + + if (NULL != pguiGroupData) + { + ::NetApiBufferFree(pguiGroupData); + } + + return hr; +} + + +/******************************************************************* + Takes a domain and name, and allocates a BSTR which represents + DOMAIN\NAME's active directory path. The BSTR this function returns + should be released manually using the ReleaseBSTR() macro. +********************************************************************/ +extern "C" HRESULT DAPI UserCreateADsPath( + __in_z LPCWSTR wzObjectDomain, + __in_z LPCWSTR wzObjectName, + __out BSTR *pbstrAdsPath + ) +{ + Assert(wzObjectDomain && wzObjectName && *wzObjectName); + + HRESULT hr = S_OK; + LPWSTR pwzAdsPath = NULL; + + hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0); + ExitOnFailure(hr, "failed to allocate AdsPath string"); + + if (*wzObjectDomain) + { + hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName); + ExitOnFailure(hr, "failed to allocate AdsPath string"); + } + else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/")) + { + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); + ExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName); + } + else + { + hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0); + ExitOnFailure(hr, "failed to concat LocalHost/"); + + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); + ExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName); + } + + *pbstrAdsPath = ::SysAllocString(pwzAdsPath); + if (NULL == *pbstrAdsPath) + { + hr = E_OUTOFMEMORY; + } + +LExit: + ReleaseStr(pwzAdsPath); + + return hr; +} + + +/******************************************************************* + Helper function to check if pwzGroupUserDomain (in form of "domain\username" is + a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the + output from both NetUserGetGroups() and NetUserGetLocalGroups() +********************************************************************/ +static BOOL CheckIsMemberHelper( + __in_z LPCWSTR pwzGroupUserDomain, + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, + __in DWORD cguiGroupData + ) +{ + if (NULL == pguiGroupData) + { + return FALSE; + } + + for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter) + { + // If the user is a member of the group, set the output flag to true + if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name)) + { + return TRUE; + } + } + + return FALSE; +} diff --git a/src/dutil/varutil.cpp b/src/dutil/varutil.cpp new file mode 100644 index 00000000..88716105 --- /dev/null +++ b/src/dutil/varutil.cpp @@ -0,0 +1,274 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +struct VARIABLE_ENUM_STRUCT +{ +}; + +struct VARIABLES_STRUCT +{ +}; + +const int VARIABLE_ENUM_HANDLE_BYTES = sizeof(VARIABLE_ENUM_STRUCT); +const int VARIABLES_HANDLE_BYTES = sizeof(VARIABLES_STRUCT); + +// function definitions + +/******************************************************************** +VarCreate - creates a variables group. +********************************************************************/ +extern "C" HRESULT DAPI VarCreate( + __out_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE* ppVariables + ) +{ + UNREFERENCED_PARAMETER(ppVariables); + return E_NOTIMPL; +} + +/******************************************************************** +VarDestroy - destroys a variables group, accepting an optional callback + to help free the variable contexts. +********************************************************************/ +extern "C" void DAPI VarDestroy( + __in_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE pVariables, + __in_opt PFN_FREEVARIABLECONTEXT vpfFreeVariableContext + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(vpfFreeVariableContext); +} + +/******************************************************************** +VarFreeValue - frees a variable value. +********************************************************************/ +extern "C" void DAPI VarFreeValue( + __in VARIABLE_VALUE* pValue + ) +{ + UNREFERENCED_PARAMETER(pValue); +} + +/******************************************************************** +VarEscapeString - escapes special characters in wzIn so that it can + be used in conditions or variable values. +********************************************************************/ +extern "C" HRESULT DAPI VarEscapeString( + __in_z LPCWSTR wzIn, + __out_z LPWSTR* psczOut + ) +{ + UNREFERENCED_PARAMETER(wzIn); + UNREFERENCED_PARAMETER(psczOut); + return E_NOTIMPL; +} + +/******************************************************************** +VarFormatString - similar to MsiFormatRecord. +********************************************************************/ +extern "C" HRESULT DAPI VarFormatString( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzIn); + UNREFERENCED_PARAMETER(psczOut); + UNREFERENCED_PARAMETER(pcchOut); + return E_NOTIMPL; +} + +/******************************************************************** +VarGetFormatted - gets the formatted value of a single variable. +********************************************************************/ +extern "C" HRESULT DAPI VarGetFormatted( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(psczValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarGetNumeric - gets the numeric value of a variable. If the type of + the variable is not numeric, it will attempt to + convert the value into a number. +********************************************************************/ +extern "C" HRESULT DAPI VarGetNumeric( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(pllValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarGetString - gets the unformatted string value of a variable. If + the type of the variable is not string, it will + convert the value to a string. +********************************************************************/ +extern "C" HRESULT DAPI VarGetString( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(psczValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarGetVersion - gets the version value of a variable. If the type of + the variable is not version, it will attempt to + convert the value into a version. +********************************************************************/ +extern "C" HRESULT DAPI VarGetVersion( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64* pqwValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(pqwValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarGetValue - gets the value of a variable along with its metadata. +********************************************************************/ +extern "C" HRESULT DAPI VarGetValue( + __in C_VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __out VARIABLE_VALUE** ppValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(ppValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarSetNumeric - sets the value of the variable to a number, the type + of the variable to numeric, and adds the variable to + the group if necessary. +********************************************************************/ +extern "C" HRESULT DAPI VarSetNumeric( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(llValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarSetString - sets the value of the variable to a string, the type + of the variable to string, and adds the variable to + the group if necessary. +********************************************************************/ +extern "C" HRESULT DAPI VarSetString( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(wzValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarSetVersion - sets the value of the variable to a version, the type + of the variable to version, and adds the variable to + the group if necessary. +********************************************************************/ +extern "C" HRESULT DAPI VarSetVersion( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64 qwValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(qwValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarSetValue - sets the value of the variable along with its metadata. + Also adds the variable to the group if necessary. +********************************************************************/ +extern "C" HRESULT DAPI VarSetValue( + __in VARIABLES_HANDLE pVariables, + __in_z LPCWSTR wzVariable, + __in VARIABLE_VALUE* pValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(wzVariable); + UNREFERENCED_PARAMETER(pValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarStartEnum - starts the enumeration of the variable group. There + is no guarantee for the order of the variable enumeration. + +NOTE: caller is responsible for calling VarFinishEnum even if function fails +********************************************************************/ +extern "C" HRESULT DAPI VarStartEnum( + __in VARIABLES_HANDLE pVariables, + __out_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE* ppEnum, + __out VARIABLE_VALUE** ppValue + ) +{ + UNREFERENCED_PARAMETER(pVariables); + UNREFERENCED_PARAMETER(ppEnum); + UNREFERENCED_PARAMETER(ppValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarNextVariable - continues the enumeration of the variable group. It + will fail if any variables were added or removed + during the enumeration. + +NOTE: caller is responsible for calling VarFinishEnum even if function fails +********************************************************************/ +extern "C" HRESULT DAPI VarNextVariable( + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum, + __out VARIABLE_VALUE** ppValue + ) +{ + UNREFERENCED_PARAMETER(pEnum); + UNREFERENCED_PARAMETER(ppValue); + return E_NOTIMPL; +} + +/******************************************************************** +VarFinishEnum - cleans up resources used for the enumeration. +********************************************************************/ +extern "C" void DAPI VarFinishEnum( + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum + ) +{ + UNREFERENCED_PARAMETER(pEnum); +} diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp new file mode 100644 index 00000000..1a489d54 --- /dev/null +++ b/src/dutil/wiutil.cpp @@ -0,0 +1,1494 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF; +const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64; + + +// structs + + +static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW; +static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW; +static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW; +static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW; +static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW; +static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW; +static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW; +static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW; +static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI; +static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW; +static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW; +static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW; + +// MSI 3.0+ +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL; +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL; +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL; +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL; +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL; +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL; +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; + +static HMODULE vhMsiDll = NULL; +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL; +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL; +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL; +static BOOL vfWiuInitialized = FALSE; + +// globals +static DWORD vdwMsiDllMajorMinor = 0; +static DWORD vdwMsiDllBuildRevision = 0; + + +// internal function declarations + +static DWORD CheckForRestartErrorCode( + __in DWORD dwErrorCode, + __out WIU_RESTART* pRestart + ); +static INT CALLBACK InstallEngineCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_z_opt LPCWSTR wzMessage + ); +static INT CALLBACK InstallEngineRecordCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_opt MSIHANDLE hRecord + ); +static INT HandleInstallMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT HandleInstallProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_z_opt LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendMsiMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendErrorMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendFilesInUseMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_opt MSIHANDLE hRecord, + __in BOOL fRestartManagerRequest + ); +static INT SendProgressUpdate( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ); +static void ResetProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ); +static DWORD CalculatePhaseProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in DWORD dwProgressIndex, + __in DWORD dwWeightPercentage + ); +void InitializeMessageData( + __in MSIHANDLE hRecord, + __out LPWSTR** prgsczData, + __out DWORD* pcData + ); +void UninitializeMessageData( + __in LPWSTR* rgsczData, + __in DWORD cData + ); + + +/******************************************************************** + WiuInitialize - initializes wiutil + +*********************************************************************/ +extern "C" HRESULT DAPI WiuInitialize( + ) +{ + HRESULT hr = S_OK; + LPWSTR sczMsiDllPath = NULL; + + hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath); + ExitOnFailure(hr, "Failed to load Msi.DLL"); + + // Ignore failures + FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision); + + vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW")); + if (NULL == vpfnMsiDeterminePatchSequenceW) + { + vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary; + } + + vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW")); + if (NULL == vpfnMsiDetermineApplicablePatchesW) + { + vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary; + } + + vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW")); + if (NULL == vpfnMsiEnumProductsExW) + { + vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary; + } + + vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW")); + if (NULL == vpfnMsiGetPatchInfoExW) + { + vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary; + } + + vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW")); + if (NULL == vpfnMsiGetProductInfoExW) + { + vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary; + } + + vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord")); + if (NULL == vpfnMsiSetExternalUIRecord) + { + vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary; + } + + //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; + vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW")); + if (NULL == vpfnMsiSourceListAddSourceExW) + { + vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary; + } + + vfWiuInitialized = TRUE; + +LExit: + ReleaseStr(sczMsiDllPath); + return hr; +} + + +/******************************************************************** + WiuUninitialize - uninitializes wiutil + +*********************************************************************/ +extern "C" void DAPI WiuUninitialize( + ) +{ + if (vhMsiDll) + { + ::FreeLibrary(vhMsiDll); + vhMsiDll = NULL; + vpfnMsiSetExternalUIRecordFromLibrary = NULL; + vpfnMsiGetProductInfoExWFromLibrary = NULL; + vpfnMsiGetPatchInfoExWFromLibrary = NULL; + vpfnMsiEnumProductsExWFromLibrary = NULL; + vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; + vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; + vpfnMsiSourceListAddSourceExWFromLibrary = NULL; + } + + vfWiuInitialized = FALSE; +} + + +/******************************************************************** + WiuFunctionOverride - overrides the Windows installer functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI WiuFunctionOverride( + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW + ) +{ + vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW; + vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW; + vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW; + vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW; + vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW; + vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW; + vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW; + vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI; + vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW; + vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW; + vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary; + vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary; + vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary; +} + + +extern "C" HRESULT DAPI WiuGetComponentPath( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + DWORD cchCompare; + + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + if (INSTALLSTATE_MOREDATA == *pInstallState) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + } + + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid argument when getting component path."); + } + else if (INSTALLSTATE_UNKNOWN == *pInstallState) + { + ExitFunction(); + } + + // If the actual path length is greater than or equal to the original buffer + // allocate a larger buffer and get the path again, just in case we are + // missing any part of the path. + if (cchCompare <= cch) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for component path."); + + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuLocateComponent( + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + DWORD cchCompare; + + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + if (INSTALLSTATE_MOREDATA == *pInstallState) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + } + + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid argument when locating component."); + } + else if (INSTALLSTATE_UNKNOWN == *pInstallState) + { + ExitFunction(); + } + + // If the actual path length is greater than or equal to the original buffer + // allocate a larger buffer and get the path again, just in case we are + // missing any part of the path. + if (cchCompare <= cch) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for component path."); + + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuQueryFeatureState( + __in_z LPCWSTR wzProduct, + __in_z LPCWSTR wzFeature, + __out INSTALLSTATE* pInstallState + ) +{ + HRESULT hr = S_OK; + + *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature); + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductInfo( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string for product info."); + + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for product info."); + + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); + } + ExitOnWin32Error(er, hr, "Failed to get product info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductInfoEx( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + if (!vpfnMsiGetProductInfoExW) + { + hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue); + ExitOnFailure(hr, "Failed to get product info when extended info was not available."); + + ExitFunction(); + } + + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string for extended product info."); + + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for extended product info."); + + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + } + ExitOnWin32Error(er, hr, "Failed to get extended product info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductProperty( + __in MSIHANDLE hProduct, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string for product property."); + + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for product property."); + + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); + } + ExitOnWin32Error(er, hr, "Failed to get product property."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetPatchInfoEx( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + if (!vpfnMsiGetPatchInfoExW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to allocate string for extended patch info."); + + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + ExitOnFailure(hr, "Failed to reallocate string for extended patch info."); + + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + } + ExitOnWin32Error(er, hr, "Failed to get extended patch info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuDeterminePatchSequence( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiDeterminePatchSequenceW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo); + ExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuDetermineApplicablePatches( + __in_z LPCWSTR wzProductPackagePath, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiDetermineApplicablePatchesW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); + ExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumProducts( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + ExitOnWin32Error(er, hr, "Failed to enumerate products."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumProductsEx( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiEnumProductsExW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + ExitOnWin32Error(er, hr, "Failed to enumerate products."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumRelatedProducts( + __in_z LPCWSTR wzUpgradeCode, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + ExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); + +LExit: + return hr; +} + +/******************************************************************** + WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code. + + Parameters: + wzUpgradeCode - The upgrade code that will be used to find the related products. + prgsczProductCodes - Pointer to the array that will contain the product codes. + pcRelatedProducts - Returns the count of the number of related products found. + fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found. +********************************************************************/ +extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( + __in_z LPCWSTR wzUpgradeCode, + __deref_out_ecount_opt(pcRelatedProducts) LPWSTR** prgsczProductCodes, + __out DWORD* pcRelatedProducts, + __in BOOL fReturnHighestVersionOnly + ) +{ + HRESULT hr = S_OK; + WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { }; + LPWSTR sczInstalledVersion = NULL; + DWORD64 qwCurrentVersion = 0; + DWORD64 qwHighestVersion = 0; + + // make sure we start at zero + *pcRelatedProducts = 0; + + for (DWORD i = 0; ; ++i) + { + hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode); + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + ExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); + + if (fReturnHighestVersionOnly) + { + // get the version + hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); + ExitOnFailure(hr, "Failed to get version for product code: %ls", wzCurrentProductCode); + + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwCurrentVersion); + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for product code: %ls", sczInstalledVersion, wzCurrentProductCode); + + // if this is the first product found then it is the highest version (for now) + if (0 == *pcRelatedProducts) + { + qwHighestVersion = qwCurrentVersion; + } + else + { + // if this is the highest version encountered so far then overwrite + // the first item in the array (there will never be more than one item) + if (qwCurrentVersion > qwHighestVersion) + { + qwHighestVersion = qwCurrentVersion; + + hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); + ExitOnFailure(hr, "Failed to update array with higher versioned product code."); + } + + // continue here as we don't want anything else added to the list + continue; + } + } + + hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0); + ExitOnFailure(hr, "Failed to add product code to array."); + } + +LExit: + ReleaseStr(sczInstalledVersion); + return hr; +} + + +extern "C" HRESULT DAPI WiuEnableLog( + __in DWORD dwLogMode, + __in_z LPCWSTR wzLogFile, + __in DWORD dwLogAttributes + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes); + ExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuInitializeExternalUI( + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in INSTALLUILEVEL internalUILevel, + __in HWND hwndParent, + __in LPVOID pvContext, + __in BOOL fRollback, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | + INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | + INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | + INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA| + INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE; + + if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor) + { + dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE; + } + + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); + pExecuteContext->fRollback = fRollback; + pExecuteContext->pfnMessageHandler = pfnMessageHandler; + pExecuteContext->pvContext = pvContext; + + // Wire the internal and external UI handler. + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); + pExecuteContext->hwndPreviousParentWindow = hwndParent; + + // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external + // UI handler if necesary. + if (vpfnMsiSetExternalUIRecord) + { + er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord); + ExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); + pExecuteContext->fSetPreviousExternalUIRecord = TRUE; + } + else + { + pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext); + pExecuteContext->fSetPreviousExternalUI = TRUE; + } + +LExit: + return hr; +} + + +extern "C" void DAPI WiuUninitializeExternalUI( + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel) + { + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow); + } + + if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler + { + vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL); + } + + if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler + { + vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL); + } + + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); +} + + +extern "C" HRESULT DAPI WiuConfigureProductEx( + __in_z LPCWSTR wzProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine); + er = CheckForRestartErrorCode(er, pRestart); + ExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuInstallProduct( + __in_z LPCWSTR wzPackagePath, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine); + er = CheckForRestartErrorCode(er, pRestart); + ExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuRemovePatches( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzPropertyList, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList); + er = CheckForRestartErrorCode(er, pRestart); + ExitOnWin32Error(er, hr, "Failed to remove patches."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuSourceListAddSourceEx( + __in_z LPCWSTR wzProductCodeOrPatchCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwCode, + __in_z LPCWSTR wzSource, + __in_opt DWORD dwIndex + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex); + ExitOnWin32Error(er, hr, "Failed to add source."); + +LExit: + return hr; +} + + + +static DWORD CheckForRestartErrorCode( + __in DWORD dwErrorCode, + __out WIU_RESTART* pRestart + ) +{ + switch (dwErrorCode) + { + case ERROR_SUCCESS_REBOOT_REQUIRED: + case ERROR_SUCCESS_RESTART_REQUIRED: + *pRestart = WIU_RESTART_REQUIRED; + dwErrorCode = ERROR_SUCCESS; + break; + + case ERROR_SUCCESS_REBOOT_INITIATED: + case ERROR_INSTALL_SUSPEND: + *pRestart = WIU_RESTART_INITIATED; + dwErrorCode = ERROR_SUCCESS; + break; + } + + return dwErrorCode; +} + +static INT CALLBACK InstallEngineCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_z_opt LPCWSTR wzMessage + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; + INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); + UINT uiFlags = 0x00FFFFFF & uiMessage; + + if (wzMessage) + { + if (INSTALLMESSAGE_PROGRESS == mt) + { + nResult = HandleInstallProgress(pContext, wzMessage, NULL); + } + else + { + nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL); + } + } + + return nResult; +} + +static INT CALLBACK InstallEngineRecordCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + HRESULT hr = S_OK; + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; + + INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); + UINT uiFlags = 0x00FFFFFF & uiMessage; + LPWSTR sczMessage = NULL; + DWORD cchMessage = 0; + + if (hRecord) + { + if (INSTALLMESSAGE_PROGRESS == mt) + { + nResult = HandleInstallProgress(pContext, NULL, hRecord); + } + else + { + // create formated message string +#pragma prefast(push) +#pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size + DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage); +#pragma prefast(pop) + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) + { + hr = StrAlloc(&sczMessage, ++cchMessage); + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + ExitOnFailure(hr, "Failed to allocate string for formated message."); + + er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage); + ExitOnWin32Error(er, hr, "Failed to format message record."); + + // Pass to handler including both the formated message and the original record. + nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord); + } + } + +LExit: + ReleaseStr(sczMessage); + return nResult; +} + +static INT HandleInstallMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + +Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage); + + // Handle the message. + switch (mt) + { + case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data + ResetProgress(pContext); + break; + + case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data + break; + + case INSTALLMESSAGE_ACTIONSTART: + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + } + + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + break; + + case INSTALLMESSAGE_ACTIONDATA: + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) + { + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; + } + else // rollback. + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; + } + + nResult = SendProgressUpdate(pContext); + } + else + { + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + } + break; + + case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough; + case INSTALLMESSAGE_FATALEXIT: __fallthrough; + case INSTALLMESSAGE_ERROR: + nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord); + break; + + case INSTALLMESSAGE_FILESINUSE: + case INSTALLMESSAGE_RMFILESINUSE: + nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt); + break; + +/* +#if 0 + case INSTALLMESSAGE_COMMONDATA: + if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2]) + { + if (L'0' == wzMessage[3]) + { + // TODO: handle the language common data message. + lres = IDOK; + return lres; + } + else if (L'1' == wzMessage[3]) + { + // TODO: really handle sending the caption. + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast(wzMessage + 3)); + return lres; + } + else if (L'2' == wzMessage[3]) + { + // TODO: really handle sending the cancel button status. + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast(wzMessage + 3)); + return lres; + } + } + break; +#endif +*/ + + //case INSTALLMESSAGE_WARNING: + //case INSTALLMESSAGE_USER: + //case INSTALLMESSAGE_INFO: + //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard + default: + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + break; + } + + // Always return "no action" (0) for resolve source messages. + return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult; +} + +static INT HandleInstallProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_z_opt LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + HRESULT hr = S_OK; + INT nResult = IDNOACTION; + INT iFields[4] = { }; + INT cFields = 0; + LPCWSTR pwz = NULL; + DWORD cch = 0; + + // get field values + if (hRecord) + { + cFields = ::MsiRecordGetFieldCount(hRecord); + cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold + for (INT i = 0; i < cFields; ++i) + { + iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1); + } + } + else + { + Assert(wzMessage); + + // parse message string + pwz = wzMessage; + while (cFields < 4) + { + // check if we have the start of a valid part + if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2]) + { + break; + } + pwz += 3; + + // find character count of number + cch = 0; + while (pwz[cch] && L' ' != pwz[cch]) + { + ++cch; + } + + // parse number + hr = StrStringToInt32(pwz, cch, &iFields[cFields]); + ExitOnFailure(hr, "Failed to parse MSI message part."); + + // increment field count + ++cFields; + } + } + +#ifdef _DEBUG + WCHAR wz[256]; + ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]); + Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz); +#endif + + // Verify that we have the enough field values. + if (1 > cFields) + { + ExitFunction(); // unknown message, bail + } + + // Handle based on message type. + switch (iFields[0]) + { + case 0: // master progress reset + if (4 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + ExitFunction(); + } + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + // Update the index into progress array. + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) + { + pContext->dwCurrentProgressIndex = 0; + } + else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress)) + { + ++pContext->dwCurrentProgressIndex; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnRootFailure(hr, "Insufficient space to hold progress information."); + } + + // we only care about the first stage after script execution has started + //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3]) + //{ + // pEngineInfo->fMsiProgressFinished = TRUE; + //} + + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1]; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]); + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]); + + if (0 == pContext->dwCurrentProgressIndex) + { + // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase + // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress + // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this + // "close" and deal with the rest. + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50; + } + break; + + case 1: // action info. + if (3 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + ExitFunction(); + } + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + if (0 == iFields[2]) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + } + else + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1]; + } + break; + + case 2: // progress report. + if (2 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + break; + } + + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) + { + break; + } + else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal) + { + break; + } + + // Update progress. + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1]; + } + else // rollback. + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1]; + } + break; + + case 3: // extend the progress bar. + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1]; + break; + + default: + ExitFunction(); // unknown message, bail + } + + // If we have a valid progress index, send an update. + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex) + { + nResult = SendProgressUpdate(pContext); + } + +LExit: + return nResult; +} + +static INT SendMsiMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; + message.dwAllowedResults = uiFlags; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.msiMessage.mt = mt; + message.msiMessage.wzMessage = wzMessage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendErrorMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + DWORD dwErrorCode = 0; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + if (hRecord) + { + dwErrorCode = ::MsiRecordGetInteger(hRecord, 1); + + // Set the recommendation if it's a known error code. + switch (dwErrorCode) + { + case 1605: // continue with install even if there isn't enough room for rollback. + nResult = IDIGNORE; + break; + + case 1704: // rollback suspended installs so our install can continue. + nResult = IDOK; + break; + } + } + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; + message.dwAllowedResults = uiFlags; + message.nResultRecommendation = nResult; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.error.dwErrorCode = dwErrorCode; + message.error.wzMessage = wzMessage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendFilesInUseMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_opt MSIHANDLE hRecord, + __in BOOL /*fRestartManagerRequest*/ + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; + message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information. + message.msiFilesInUse.rgwzFiles = message.rgwzData; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendProgressUpdate( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ) +{ + int nResult = IDNOACTION; + DWORD dwPercentage = 0; // number representing 0 - 100% + WIU_MSI_EXECUTE_MESSAGE message = { }; + + //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal; + //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete); + //double dProgressGauge = 0; + //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal; + + // Calculate progress for the phases of Windows Installer. + // TODO: handle upgrade progress which would add another phase. + dwPercentage += CalculatePhaseProgress(pContext, 0, 15); + dwPercentage += CalculatePhaseProgress(pContext, 1, 80); + dwPercentage += CalculatePhaseProgress(pContext, 2, 5); + dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%. + + if (pContext->fRollback) + { + dwPercentage = 100 - dwPercentage; + } + + //if (qwTotal) // avoid "divide by zero" if the MSI range is blank. + //{ + // // calculate gauge. + // double dProgressGauge = static_cast(qwCompleted) / static_cast(qwTotal); + // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804; + // qwCompleted = (DWORD)(dProgressGauge * qwTotal); + + // // calculate progress within range + // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal)); + // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal); + //} + +#ifdef _DEBUG + DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted; + DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal; + Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage); + //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress."); +#endif + + message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = dwPercentage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + return nResult; +} + +static void ResetProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ) +{ + memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress)); + pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID; +} + +static DWORD CalculatePhaseProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in DWORD dwProgressIndex, + __in DWORD dwWeightPercentage + ) +{ + DWORD dwPhasePercentage = 0; + + // If we've already passed this progress index, return the maximum percentage possible (the weight) + if (dwProgressIndex < pContext->dwCurrentProgressIndex) + { + dwPhasePercentage = dwWeightPercentage; + } + else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress. + { + WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex; + if (pProgress->dwTotal) + { + DWORD64 dw64Completed = pProgress->dwCompleted; + dwPhasePercentage = static_cast(dw64Completed * dwWeightPercentage / pProgress->dwTotal); + } + } + // else we're not there yet so it has to be zero. + + return dwPhasePercentage; +} + +void InitializeMessageData( + __in_opt MSIHANDLE hRecord, + __deref_out_ecount(*pcData) LPWSTR** prgsczData, + __out DWORD* pcData + ) +{ + DWORD cData = 0; + LPWSTR* rgsczData = NULL; + + // If we have a record based message, try to get the extra data. + if (hRecord) + { + cData = ::MsiRecordGetFieldCount(hRecord); + if (cData) + { + rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE); + } + + for (DWORD i = 0; rgsczData && i < cData; ++i) + { + DWORD cch = 0; + + // get string from record +#pragma prefast(push) +#pragma prefast(disable:6298) + DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch); +#pragma prefast(pop) + if (ERROR_MORE_DATA == er) + { + HRESULT hr = StrAlloc(&rgsczData[i], ++cch); + if (SUCCEEDED(hr)) + { + er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch); + } + } + } + } + + *prgsczData = rgsczData; + *pcData = cData; +} + +void UninitializeMessageData( + __in LPWSTR* rgsczData, + __in DWORD cData + ) +{ + // Clean up if there was any data allocated. + if (rgsczData) + { + for (DWORD i = 0; i < cData; ++i) + { + ReleaseStr(rgsczData[i]); + } + + MemFree(rgsczData); + } +} diff --git a/src/dutil/wuautil.cpp b/src/dutil/wuautil.cpp new file mode 100644 index 00000000..94ab659d --- /dev/null +++ b/src/dutil/wuautil.cpp @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT GetAutomaticUpdatesService( + __out IAutomaticUpdates **ppAutomaticUpdates + ); + + +// function definitions + +extern "C" HRESULT DAPI WuaPauseAutomaticUpdates() +{ + HRESULT hr = S_OK; + IAutomaticUpdates *pAutomaticUpdates = NULL; + + hr = GetAutomaticUpdatesService(&pAutomaticUpdates); + ExitOnFailure(hr, "Failed to get the Automatic Updates service."); + + hr = pAutomaticUpdates->Pause(); + ExitOnFailure(hr, "Failed to pause the Automatic Updates service."); + +LExit: + ReleaseObject(pAutomaticUpdates); + + return hr; +} + +extern "C" HRESULT DAPI WuaResumeAutomaticUpdates() +{ + HRESULT hr = S_OK; + IAutomaticUpdates *pAutomaticUpdates = NULL; + + hr = GetAutomaticUpdatesService(&pAutomaticUpdates); + ExitOnFailure(hr, "Failed to get the Automatic Updates service."); + + hr = pAutomaticUpdates->Resume(); + ExitOnFailure(hr, "Failed to resume the Automatic Updates service."); + +LExit: + ReleaseObject(pAutomaticUpdates); + + return hr; +} + +extern "C" HRESULT DAPI WuaRestartRequired( + __out BOOL* pfRestartRequired + ) +{ + HRESULT hr = S_OK; + ISystemInformation* pSystemInformation = NULL; + VARIANT_BOOL bRestartRequired; + + hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast(&pSystemInformation)); + ExitOnRootFailure(hr, "Failed to get WUA system information interface."); + + hr = pSystemInformation->get_RebootRequired(&bRestartRequired); + ExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); + + *pfRestartRequired = (VARIANT_FALSE != bRestartRequired); + +LExit: + ReleaseObject(pSystemInformation); + + return hr; +} + + +// internal function definitions + +static HRESULT GetAutomaticUpdatesService( + __out IAutomaticUpdates **ppAutomaticUpdates + ) +{ + HRESULT hr = S_OK; + CLSID clsidAutomaticUpdates = { }; + + hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates); + ExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate."); + + hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast(ppAutomaticUpdates)); + ExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate."); + +LExit: + return hr; +} diff --git a/src/dutil/xmlutil.cpp b/src/dutil/xmlutil.cpp new file mode 100644 index 00000000..e07c205d --- /dev/null +++ b/src/dutil/xmlutil.cpp @@ -0,0 +1,1317 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// intialization globals +CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} }; +static volatile LONG vcXmlInitialized = 0; +static BOOL vfMsxml40 = FALSE; +static BOOL fComInitialized = FALSE; +BOOL vfMsxml30 = FALSE; + +/******************************************************************** + XmlInitialize - finds an appropriate version of the XML DOM + +*********************************************************************/ +extern "C" HRESULT DAPI XmlInitialize( + ) +{ + HRESULT hr = S_OK; + + if (!fComInitialized) + { + hr = ::CoInitialize(0); + if (RPC_E_CHANGED_MODE != hr) + { + ExitOnFailure(hr, "failed to initialize COM"); + fComInitialized = TRUE; + } + } + + LONG cInitialized = ::InterlockedIncrement(&vcXmlInitialized); + if (1 == cInitialized) + { + // NOTE: 4.0 behaves differently than 3.0 so there may be problems doing this +#if 0 + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument.4.0", &vclsidXMLDOM); + if (S_OK == hr) + { + vfMsxml40 = TRUE; + Trace(REPORT_VERBOSE, "found Msxml2.DOMDocument.4.0"); + ExitFunction(); + } +#endif + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument", &vclsidXMLDOM); + if (FAILED(hr)) + { + // try to fall back to old MSXML + hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM); + } + ExitOnFailure(hr, "failed to get CLSID for XML DOM"); + + Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument26) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument40) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument50) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument60)); + } + + hr = S_OK; +LExit: + return hr; +} + + +/******************************************************************** + XmUninitialize - + +*********************************************************************/ +extern "C" void DAPI XmlUninitialize( + ) +{ + AssertSz(vcXmlInitialized, "XmlUninitialize called when not initialized"); + + LONG cInitialized = ::InterlockedDecrement(&vcXmlInitialized); + + if (0 == cInitialized) + { + memset(&vclsidXMLDOM, 0, sizeof(vclsidXMLDOM)); + + if (fComInitialized) + { + ::CoUninitialize(); + } + } +} + +extern "C" HRESULT DAPI XmlCreateElement( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzElementName, + __out IXMLDOMElement **ppixnElement + ) +{ + if (!ppixnElement || !pixdDocument) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrElementName = ::SysAllocString(wzElementName); + ExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + hr = pixdDocument->createElement(bstrElementName, ppixnElement); +LExit: + ReleaseBSTR(bstrElementName); + return hr; +} + + +/******************************************************************** + XmlCreateDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateDocument( + __in_opt LPCWSTR pwzElementName, + __out IXMLDOMDocument** ppixdDocument, + __out_opt IXMLDOMElement** ppixeRootElement + ) +{ + HRESULT hr = S_OK; + BOOL (WINAPI *pfnDisableWow64)(__out PVOID* ) = NULL; + BOOLEAN (WINAPI *pfnEnableWow64)(__in BOOLEAN ) = NULL; + BOOL (WINAPI *pfnRevertWow64)(__in PVOID ) = NULL; + BOOL fWow64Available = FALSE; + void *pvWow64State = NULL; + + // RELEASEME + IXMLDOMElement* pixeRootElement = NULL; + IXMLDOMDocument *pixdDocument = NULL; + + // Test if we have access to the Wow64 API, and store the result in fWow64Available + HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll"); + ExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll"); + + // This will test if we have access to the Wow64 API + if (NULL != GetProcAddress(hKernel32, "IsWow64Process")) + { + pfnDisableWow64 = (BOOL (WINAPI *)(PVOID *))::GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection"); + pfnEnableWow64 = (BOOLEAN (WINAPI *)(BOOLEAN))::GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection"); + pfnRevertWow64 = (BOOL (WINAPI *)(PVOID))::GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection"); + + fWow64Available = pfnDisableWow64 && pfnEnableWow64 && pfnRevertWow64; + } + + // create the top level XML document + AssertSz(vcXmlInitialized, "XmlInitialize() was not called"); + + // Enable Wow64 Redirection, if possible + if (fWow64Available) + { + // 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) + pfnDisableWow64(&pvWow64State); + // If we fail to enable it, don't bother trying to disable it later on + fWow64Available = pfnEnableWow64(TRUE); + } + + hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument); + ExitOnFailure(hr, "failed to create XML DOM Document"); + Assert(pixdDocument); + + if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20)) + { + vfMsxml30 = TRUE; + } + + if (pwzElementName) + { + hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement); + ExitOnFailure(hr, "failed XmlCreateElement"); + hr = pixdDocument->appendChild(pixeRootElement, NULL); + ExitOnFailure(hr, "failed appendChild"); + } + + *ppixdDocument = pixdDocument; + pixdDocument = NULL; + + if (ppixeRootElement) + { + *ppixeRootElement = pixeRootElement; + pixeRootElement = NULL; + } + +LExit: + // Re-disable Wow64 Redirection, if appropriate + if (fWow64Available && !pfnRevertWow64(pvWow64State)) + { + // If we expected to be able to revert, and couldn't, fail in the only graceful way we can + ::ExitProcess(1); + } + + ReleaseObject(pixeRootElement); + ReleaseObject(pixdDocument); + return hr; +} + + +/******************************************************************** + XmlLoadDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocument( + __in_z LPCWSTR wzDocument, + __out IXMLDOMDocument** ppixdDocument + ) +{ + return XmlLoadDocumentEx(wzDocument, 0, ppixdDocument); +} + + +/******************************************************************** + XmlReportParseError - + +*********************************************************************/ +static void XmlReportParseError( + __in IXMLDOMParseError* pixpe + ) +{ + HRESULT hr = S_OK; + long lNumber = 0; + BSTR bstr = NULL; + + Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:"); + + hr = pixpe->get_errorCode(&lNumber); + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode."); + Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber); + + hr = pixpe->get_filepos(&lNumber); + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos."); + Trace(REPORT_STANDARD, "filepos = %d", lNumber); + + hr = pixpe->get_line(&lNumber); + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.line."); + Trace(REPORT_STANDARD, "line = %d", lNumber); + + hr = pixpe->get_linepos(&lNumber); + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos."); + Trace(REPORT_STANDARD, "linepos = %d", lNumber); + + hr = pixpe->get_reason(&bstr); + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason."); + Trace(REPORT_STANDARD, "reason = %ls", bstr); + ReleaseNullBSTR(bstr); + + hr = pixpe->get_srcText (&bstr); + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText ."); + Trace(REPORT_STANDARD, "srcText = %ls", bstr); + ReleaseNullBSTR(bstr); + +LExit: + ReleaseBSTR(bstr); +} + +/******************************************************************** + XmlLoadDocumentEx - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentEx( + __in_z LPCWSTR wzDocument, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + VARIANT_BOOL vbSuccess = 0; + + // RELEASEME + IXMLDOMDocument* pixd = NULL; + IXMLDOMParseError* pixpe = NULL; + BSTR bstrLoad = NULL; + + if (!wzDocument || !*wzDocument) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "string must be non-null"); + } + + hr = XmlCreateDocument(NULL, &pixd); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed XmlCreateDocument"); + + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) + { + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); + ExitOnFailure(hr, "failed put_preserveWhiteSpace"); + } + + // Security issue. Avoid triggering anything external. + hr = pixd->put_validateOnParse(VARIANT_FALSE); + ExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixd->put_resolveExternals(VARIANT_FALSE); + ExitOnFailure(hr, "failed put_resolveExternals"); + + bstrLoad = ::SysAllocString(wzDocument); + ExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx"); + + hr = pixd->loadXML(bstrLoad, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) + { + XmlReportParseError(pixpe); + } + + ExitOnFailure(hr, "failed loadXML"); + + + hr = S_OK; +LExit: + if (ppixdDocument) + { + *ppixdDocument = pixd; + pixd = NULL; + } + ReleaseBSTR(bstrLoad); + ReleaseObject(pixd); + ReleaseObject(pixpe); + + return hr; +} + + +/******************************************************************* + XmlLoadDocumentFromFile + +********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromFile( + __in_z LPCWSTR wzPath, + __out IXMLDOMDocument** ppixdDocument + ) +{ + return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument); +} + + +/******************************************************************* + XmlLoadDocumentFromFileEx + +********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx( + __in_z LPCWSTR wzPath, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + VARIANT varPath; + VARIANT_BOOL vbSuccess = 0; + + IXMLDOMDocument* pixd = NULL; + IXMLDOMParseError* pixpe = NULL; + + ::VariantInit(&varPath); + varPath.vt = VT_BSTR; + varPath.bstrVal = ::SysAllocString(wzPath); + ExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx"); + + hr = XmlCreateDocument(NULL, &pixd); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed XmlCreateDocument"); + + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) + { + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); + ExitOnFailure(hr, "failed put_preserveWhiteSpace"); + } + + // Avoid triggering anything external. + hr = pixd->put_validateOnParse(VARIANT_FALSE); + ExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixd->put_resolveExternals(VARIANT_FALSE); + ExitOnFailure(hr, "failed put_resolveExternals"); + + pixd->put_async(VARIANT_FALSE); + hr = pixd->load(varPath, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) + { + XmlReportParseError(pixpe); + } + + ExitOnFailure(hr, "failed to load XML from: %ls", wzPath); + + if (ppixdDocument) + { + *ppixdDocument = pixd; + pixd = NULL; + } + + hr = S_OK; +LExit: + ReleaseVariant(varPath); + ReleaseObject(pixd); + ReleaseObject(pixpe); + + return hr; +} + + +/******************************************************************** + XmlLoadDocumentFromBuffer + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( + __in_bcount(cbSource) const BYTE* pbSource, + __in DWORD cbSource, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + SAFEARRAY sa = { }; + VARIANT vtXmlSource; + VARIANT_BOOL vbSuccess = 0; + + ::VariantInit(&vtXmlSource); + + // create document + hr = XmlCreateDocument(NULL, &pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed XmlCreateDocument"); + + // Security issue. Avoid triggering anything external. + hr = pixdDocument->put_validateOnParse(VARIANT_FALSE); + ExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixdDocument->put_resolveExternals(VARIANT_FALSE); + ExitOnFailure(hr, "failed put_resolveExternals"); + + // load document + sa.cDims = 1; + sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE; + sa.cbElements = 1; + sa.pvData = (PVOID)pbSource; + sa.rgsabound[0].cElements = cbSource; + vtXmlSource.vt = VT_ARRAY | VT_UI1; + vtXmlSource.parray = &sa; + + hr = pixdDocument->load(vtXmlSource, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + ExitOnFailure(hr, "failed loadXML"); + + // return value + *ppixdDocument = pixdDocument; + pixdDocument = NULL; + +LExit: + ReleaseObject(pixdDocument); + return hr; +} + + +/******************************************************************** + XmlSetAttribute - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in_z LPCWSTR pwzAttributeValue + ) +{ + HRESULT hr = S_OK; + VARIANT varAttributeValue; + ::VariantInit(&varAttributeValue); + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMAttribute* pixaAttribute = NULL; + IXMLDOMNode* pixaNode = NULL; + BSTR bstrAttributeName = ::SysAllocString(pwzAttribute); + ExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute"); + + hr = pixnNode->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute); + + hr = pixnNode->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + + hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute); + ExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute); + + varAttributeValue.vt = VT_BSTR; + varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue); + if (!varAttributeValue.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + ExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute"); + + hr = pixaAttribute->put_nodeValue(varAttributeValue); + ExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute); + + hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode); + ExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute); + +LExit: + ReleaseObject(pixdDocument); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixaAttribute); + ReleaseObject(pixaNode); + ReleaseBSTR(varAttributeValue.bstrVal); + ReleaseBSTR(bstrAttributeName); + + return hr; +} + + +/******************************************************************** + XmlSelectSingleNode - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSelectSingleNode( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNode **ppixnChild + ) +{ + HRESULT hr = S_OK; + + BSTR bstrXPath = NULL; + + ExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode"); + ExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode"); + + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); + ExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode"); + + hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild); + +LExit: + ReleaseBSTR(bstrXPath); + + return hr; +} + + +/******************************************************************** + XmlCreateTextNode - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateTextNode( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzText, + __out IXMLDOMText **ppixnTextNode + ) +{ + if (!ppixnTextNode || !pixdDocument) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrText = ::SysAllocString(wzText); + ExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString"); + hr = pixdDocument->createTextNode(bstrText, ppixnTextNode); +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + + +/******************************************************************** + XmlGetText + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetText( + __in IXMLDOMNode* pixnNode, + __deref_out_z BSTR* pbstrText + ) +{ + return pixnNode->get_text(pbstrText); +} + + +/******************************************************************** + XmlGetAttribute + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __deref_out_z BSTR* pbstrAttributeValue + ) +{ + Assert(pixnNode); + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + VARIANT varAttributeValue; + BSTR bstrAttribute = SysAllocString(pwzAttribute); + + // INIT + ::VariantInit(&varAttributeValue); + + // get attribute value from source + hr = pixnNode->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "failed get_attributes"); + + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); + if (S_FALSE == hr) + { + // hr = E_FAIL; + ExitFunction(); + } + ExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute); + + hr = pixnAttribute->get_nodeValue(&varAttributeValue); + ExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute); + + // steal the BSTR from the VARIANT + if (S_OK == hr && pbstrAttributeValue) + { + *pbstrAttributeValue = varAttributeValue.bstrVal; + varAttributeValue.bstrVal = NULL; + } + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + ReleaseVariant(varAttributeValue); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlGetAttributeEx + +*********************************************************************/ +HRESULT DAPI XmlGetAttributeEx( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __deref_out_z LPWSTR* psczAttributeValue + ) +{ + Assert(pixnNode); + HRESULT hr = S_OK; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + VARIANT varAttributeValue; + BSTR bstrAttribute = NULL; + + ::VariantInit(&varAttributeValue); + + // get attribute value from source + hr = pixnNode->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "Failed get_attributes."); + + bstrAttribute = ::SysAllocString(wzAttribute); + ExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR."); + + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute); + + hr = pixnAttribute->get_nodeValue(&varAttributeValue); + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute); + + // copy value + hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0); + ExitOnFailure(hr, "Failed to copy attribute value."); + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + ReleaseVariant(varAttributeValue); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlGetYesNoAttribute + +*********************************************************************/ +HRESULT DAPI XmlGetYesNoAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __out BOOL* pfYes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get attribute."); + + *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1); + } + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + + +/******************************************************************** + XmlGetAttributeNumber + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD* pdwValue + ) +{ + HRESULT hr = XmlGetAttributeNumberBase(pixnNode, pwzAttribute, 10, pdwValue); + return hr; +} + + +/******************************************************************** + XmlGetAttributeNumberBase + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeNumberBase( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in int nBase, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + BSTR bstrPointer = NULL; + + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer); + ExitOnFailure(hr, "Failed to get value from attribute."); + + if (S_OK == hr) + { + *pdwValue = wcstoul(bstrPointer, NULL, nBase); + } + +LExit: + ReleaseBSTR(bstrPointer); + return hr; +} + + +/******************************************************************** + XmlGetAttributeLargeNumber + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeLargeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD64* pdw64Value + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue); + ExitOnFailure(hr, "failed XmlGetAttribute"); + + if (S_OK == hr) + { + LONGLONG ll = 0; + hr = StrStringToInt64(bstrValue, 0, &ll); + ExitOnFailure(hr, "Failed to treat attribute value as number."); + + *pdw64Value = ll; + } + else + { + *pdw64Value = 0; + } + +LExit: + ReleaseBSTR(bstrValue); + return hr; +} + + +/******************************************************************** + XmlGetNamedItem - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetNamedItem( + __in IXMLDOMNamedNodeMap *pixnmAttributes, + __in_opt LPCWSTR wzName, + __out IXMLDOMNode **ppixnNamedItem + ) +{ + if (!pixnmAttributes || !ppixnNamedItem) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrName = ::SysAllocString(wzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem); + +LExit: + ReleaseBSTR(bstrName); + return hr; +} + + +/******************************************************************** + XmlSetText - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetText( + __in IXMLDOMNode *pixnNode, + __in_z LPCWSTR pwzText + ) +{ + Assert(pixnNode && pwzText); + HRESULT hr = S_OK; + DOMNodeType dnType; + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNodeList* pixnlNodeList = NULL; + IXMLDOMNode* pixnChildNode = NULL; + IXMLDOMText* pixtTextNode = NULL; + VARIANT varText; + + ::VariantInit(&varText); + + // find the text node + hr = pixnNode->get_childNodes(&pixnlNodeList); + ExitOnFailure(hr, "failed to get child nodes"); + + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode))) + { + hr = pixnChildNode->get_nodeType(&dnType); + ExitOnFailure(hr, "failed to get node type"); + + if (NODE_TEXT == dnType) + break; + ReleaseNullObject(pixnChildNode); + } + if (S_FALSE == hr) + { + hr = S_OK; + } + + if (pixnChildNode) + { + varText.vt = VT_BSTR; + varText.bstrVal = ::SysAllocString(pwzText); + if (!varText.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + ExitOnFailure(hr, "failed SysAllocString in XmlSetText"); + + hr = pixnChildNode->put_nodeValue(varText); + ExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue"); + } + else + { + hr = pixnNode->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + + hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode); + ExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText); + + hr = pixnNode->appendChild(pixtTextNode, NULL); + ExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText); + } + + hr = *pwzText ? S_OK : S_FALSE; + +LExit: + ReleaseObject(pixnlNodeList); + ReleaseObject(pixnChildNode); + ReleaseObject(pixdDocument); + ReleaseObject(pixtTextNode); + ReleaseVariant(varText); + return hr; +} + + +/******************************************************************** + XmlSetTextNumber - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetTextNumber( + __in IXMLDOMNode *pixnNode, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + WCHAR wzValue[12]; + + hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue); + ExitOnFailure(hr, "Failed to format numeric value as string."); + + hr = XmlSetText(pixnNode, wzValue); + +LExit: + return hr; +} + + +/******************************************************************** + XmlCreateChild - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateChild( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR pwzElementType, + __out IXMLDOMNode** ppixnChild + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNode* pixnChild = NULL; + + hr = pixnParent->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed get_ownerDocument"); + + hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed createElement"); + + pixnParent->appendChild(pixnChild,NULL); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed appendChild"); + + if (ppixnChild) + { + *ppixnChild = pixnChild; + pixnChild = NULL; + } + +LExit: + ReleaseObject(pixdDocument); + ReleaseObject(pixnChild); + return hr; +} + +/******************************************************************** + XmlRemoveAttribute - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlRemoveAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + BSTR bstrAttribute = ::SysAllocString(pwzAttribute); + ExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute"); + + hr = pixnNode->get_attributes(&pixnnmAttributes); + ExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute); + + hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL); + ExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute); + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlSelectNodes - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSelectNodes( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNodeList **ppixnlChildren + ) +{ + HRESULT hr = S_OK; + + BSTR bstrXPath = NULL; + + ExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes"); + ExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes"); + + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); + ExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes"); + + hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren); + +LExit: + ReleaseBSTR(bstrXPath); + return hr; +} + + +/******************************************************************** + XmlNextAttribute - returns the next attribute in a node list + + NOTE: pbstrAttribute is optional + returns S_OK if found an element + returns S_FALSE if no element found + returns E_* if something went wrong +********************************************************************/ +extern "C" HRESULT DAPI XmlNextAttribute( + __in IXMLDOMNamedNodeMap* pixnnm, + __out IXMLDOMNode** pixnAttribute, + __deref_opt_out_z_opt BSTR* pbstrAttribute + ) +{ + Assert(pixnnm && pixnAttribute); + + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DOMNodeType nt; + + // null out the return values + *pixnAttribute = NULL; + if (pbstrAttribute) + { + *pbstrAttribute = NULL; + } + + hr = pixnnm->nextNode(&pixn); + ExitOnFailure(hr, "Failed to get next attribute."); + + if (S_OK == hr) + { + hr = pixn->get_nodeType(&nt); + ExitOnFailure(hr, "failed to get node type"); + + if (NODE_ATTRIBUTE != nt) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Failed to get expected node type back: attribute"); + } + + // if the caller asked for the attribute name + if (pbstrAttribute) + { + hr = pixn->get_baseName(pbstrAttribute); + ExitOnFailure(hr, "failed to get attribute name"); + } + + *pixnAttribute = pixn; + pixn = NULL; + } + +LExit: + ReleaseObject(pixn); + return hr; +} + + +/******************************************************************** + XmlNextElement - returns the next element in a node list + + NOTE: pbstrElement is optional + returns S_OK if found an element + returns S_FALSE if no element found + returns E_* if something went wrong +********************************************************************/ +extern "C" HRESULT DAPI XmlNextElement( + __in IXMLDOMNodeList* pixnl, + __out IXMLDOMNode** pixnElement, + __deref_opt_out_z_opt BSTR* pbstrElement + ) +{ + Assert(pixnl && pixnElement); + + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DOMNodeType nt; + + // null out the return values + *pixnElement = NULL; + if (pbstrElement) + { + *pbstrElement = NULL; + } + + // + // find the next element in the list + // + while (S_OK == (hr = pixnl->nextNode(&pixn))) + { + hr = pixn->get_nodeType(&nt); + ExitOnFailure(hr, "failed to get node type"); + + if (NODE_ELEMENT == nt) + break; + + ReleaseNullObject(pixn); + } + ExitOnFailure(hr, "failed to get next element"); + + // if we have a node and the caller asked for the element name + if (pixn && pbstrElement) + { + hr = pixn->get_baseName(pbstrElement); + ExitOnFailure(hr, "failed to get element name"); + } + + *pixnElement = pixn; + pixn = NULL; + + hr = *pixnElement ? S_OK : S_FALSE; +LExit: + ReleaseObject(pixn); + return hr; +} + + +/******************************************************************** + XmlRemoveChildren - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlRemoveChildren( + __in IXMLDOMNode* pixnSource, + __in_z LPCWSTR pwzXPath + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNodeList* pixnlNodeList = NULL; + IXMLDOMNode* pixnNode = NULL; + IXMLDOMNode* pixnRemoveChild = NULL; + + if (pwzXPath) + { + hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList); + ExitOnFailure(hr, "failed XmlSelectNodes"); + } + else + { + hr = pixnSource->get_childNodes(&pixnlNodeList); + ExitOnFailure(hr, "failed childNodes"); + } + if (S_FALSE == hr) + { + ExitFunction(); + } + + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode))) + { + hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild); + ExitOnFailure(hr, "failed removeChild"); + + ReleaseNullObject(pixnRemoveChild); + ReleaseNullObject(pixnNode); + } + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseObject(pixnlNodeList); + ReleaseObject(pixnNode); + ReleaseObject(pixnRemoveChild); + + return hr; +} + + +/******************************************************************** + XmlSaveDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSaveDocument( + __in IXMLDOMDocument* pixdDocument, + __inout LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + VARIANT varsDestPath; + + ::VariantInit(&varsDestPath); + varsDestPath.vt = VT_BSTR; + varsDestPath.bstrVal = ::SysAllocString(wzPath); + if (!varsDestPath.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + ExitOnFailure(hr, "failed to create BSTR"); + + hr = pixdDocument->save(varsDestPath); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "failed save in WriteDocument"); + +LExit: + ReleaseVariant(varsDestPath); + return hr; +} + + +/******************************************************************** + XmlSaveDocumentToBuffer + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSaveDocumentToBuffer( + __in IXMLDOMDocument* pixdDocument, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out DWORD* pcbDest + ) +{ + HRESULT hr = S_OK; + IStream* pStream = NULL; + LARGE_INTEGER li = { }; + STATSTG statstg = { }; + BYTE* pbDest = NULL; + ULONG cbRead = 0; + VARIANT vtDestination; + + ::VariantInit(&vtDestination); + + // create stream + hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ExitOnFailure(hr, "Failed to create stream."); + + // write document to stream + vtDestination.vt = VT_UNKNOWN; + vtDestination.punkVal = (IUnknown*)pStream; + hr = pixdDocument->save(vtDestination); + ExitOnFailure(hr, "Failed to save document."); + + // get stream size + hr = pStream->Stat(&statstg, STATFLAG_NONAME); + ExitOnFailure(hr, "Failed to get stream size."); + + // allocate buffer + pbDest = static_cast(MemAlloc((SIZE_T)statstg.cbSize.LowPart, TRUE)); + ExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); + + // read data from stream + li.QuadPart = 0; + hr = pStream->Seek(li, STREAM_SEEK_SET, NULL); + ExitOnFailure(hr, "Failed to seek stream."); + + hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead); + if (cbRead < statstg.cbSize.LowPart) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "Failed to read stream content to buffer."); + + // return value + *ppbDest = pbDest; + pbDest = NULL; + *pcbDest = statstg.cbSize.LowPart; + +LExit: + ReleaseObject(pStream); + ReleaseMem(pbDest); + return hr; +} diff --git a/src/dutil/xsd/thmutil.xsd b/src/dutil/xsd/thmutil.xsd new file mode 100644 index 00000000..ccf951c0 --- /dev/null +++ b/src/dutil/xsd/thmutil.xsd @@ -0,0 +1,1188 @@ + + + + + + + + Schema for describing Theme files processed by thmutil. + + + + + + + + + This is the top-level container element for every thmutil Theme file. + + + + + + + + + + + Relative path to an image file that can serve as a single source for images in the rest of the theme. + This image is referenced by controls using the SourceX and SourceY attributes. + Mutually exclusive with the ImageResource attribute. + + + + + + + Identifier that references an image resource in the module for the window. + Mutually exclusive with the ImageFile attribute. + + + + + + + + + Defines a font including the size and color. + + + + + + Name of the font face (required). + + + + 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. + + + + + Font size. Use negative numbers to specify the font in pixels. + + + + + Font weight. + + + + + + A system color id or a hexadecimal value representing BGR foreground color of the font. + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. + If this attribute is absent the foreground will be transparent. + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. + + + + + + + A system color id or a hexadecimal value representing BGR background color of the font. + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. + If this attribute is absent the background will be transparent. + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. + + + + + + Specifies whether the font is underlined. + + + + + + + + + + List of images which can be shared between multiple controls. + + + + + + + + + Name of the ImageList, to be referenced by other controls. + + + + + + + + + Named set of controls that can be shown and hidden collectively. + + + + + + + Optional name for the page. + + + + + + + + + Defines the overall look of the main window. + + + + + + + + + + Specifies whether the ThmUtil default window proc should process WM_SIZE and WM_SIZING events. + + + + + + Caption for the window. + This is required if not using the StringId attribute. + + + + + + Numeric identifier to the Font element that serves as the default font for the window. + + + + + Height of the window. + + + + + + Hexadecimal window style. If this is not specified the default value is: WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU. + If SourceX and SourceY are specified, then WS_OVERLAPPED is replaced with WS_POPUP. + + + + + + Relative path to an icon file for the window. Mutually exclusive with IconResource and SourceX and SourceY attributes. + + + + + + Identifier that references an icon resource in the module for the icon for the window. + Mutually exclusive with IconFile and SourceX and SourceY attributes. + + + + + + Minimum height of the window. Only functions if AutoResize is enabled. + + + + + Minimum width of the window. Only functions if AutoResize is enabled. + + + + + X offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. + + + + + Y offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. + + + + + + Identifier that references a string resource in the module to define the window caption. + Mutually exclusive with the Caption attribute. + + + + + + Width of the window. + + + + + + + + Defines a control that rotates through a set of images on a specified interval. + + + + + + + + + + Specifies the time to wait before showing the next image, in milliseconds. + + + + + + Specifies whether the billboard should loop through the images infinitely. + + + + + + + + Defines a button. + + + + + Text to display in the button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + 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). + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. + + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. + + + + + Numeric identifier to the Font element that serves as the font when the control is hovered over. Only valid when using graphic buttons. + + + + + + Relative path to an image file to define a graphic button. + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an image resource in the module to define a graphic button. + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + Numeric identifier to the Font element that serves as the font when the control is selected. Only valid when using graphic buttons. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + + When the button is pressed, a directory browser dialog is shown. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + The name of the variable to update when the user selects a directory from the dialog. + + + + + + + + + + When the button is pressed, the specified page is shown. + + + + + + + When set to 'yes', none of the variable changes made on the current page are saved. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + The Name of the Page to show. + + + + + + + + + + When the button is pressed, the WM_CLOSE message is sent to the window. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + + + Defines a checkbox. + + + + + Text to display beside the checkbox. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines a combobox. + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + + Defines a button. + + + + + Text to display in the button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + 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). + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. + + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. + + + + + + Relative path to an icon file to define a command link glyph. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an icon resource in the module to define a command link glyph. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + Relative path to an image file to define a command link glyph. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an image resource in the module to define a command link glyph. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines an edit box. + + + + + + + Initial text for the control. + Mutually exclusive with the StringId attribute. + + + + + + Specifies whether the edit box should auto-complete with file system paths. + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the initial text for the control. + + + + + + + + + + + Defines a hyperlink. + + + + + Text to display as the link. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the unselected font. + + + + + Numeric identifier to the Font element that serves as the font when the control is hovered over. + + + + + Numeric identifier to the Font element that serves as the font when the control is selected. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines a text block with support for HTML <a> tags. + + + + + Text to display as the link. + Use HTML <a href="URL"> to create a link. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines an image for an ImageList or Billboard. + + + + + Relative path to an image file. Mutually exclusive with ImageResource. + + + + + Identifier that references an image resource in the module. Mutually exclusive with ImageFile. + + + + + + + + Defines an image. + + + + + + Relative path to an image file. Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + Identifier that references an image resource in the module. Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + + Defines a label. + + + + + Text for the label to display. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Specifies whether the text should be centered horizontally in the width of the control. Default is "no". + + + + + By default ampersands (&) 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". + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the label. + + + + + + + + + Defines a listview. + + + + + + + + + Numeric identifier to the Font element that serves as the default font for the ListView. + + + + + Hexadecimal extended window style. + + + + + + The name of the ImageList to assign to this listview with type LVSIL_NORMAL. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_SMALL. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_STATE. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_GROUPHEADER. + + + + + + + + + + Defines note text for a command link control based on an optional condition. + 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). + 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. + + + + + + + + Note text for the parent command link control. + + + + + + The condition that determines when the parent control will use this note text. + + + + + + + + + + + Defines a collection of controls. + + + + + + + + + + Defines a progress bar. + + + + + + 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. + + + + + 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. + + + + + + + + Defines an individual radio button within a set of radio buttons. + + + + + Text to display beside the radio button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + Optional value used when setting the variable associated with the set of radio buttons. + + + + + + + + Defines a set of radio buttons. + + + + + + + + Optional variable name for the set of radio buttons. + + + + + + + + Defines a rich edit control. + + + + + Initial text for the control. + Mutually exclusive with the StringId attribute. + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + Identifier that references a string resource in the module to define the initial text for the control. + + + + + + + + + Defines a straight line. + + + + + + + + + Defines an individual tab within a set of tabs. + + + + + + + Caption of the tab. + Mutually exclusive with the StringId attribute. + + + + + + Identifier that references a string resource in the module to define the caption of the tab. + + + + + + + + + + + Defines a set of tabs. + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + + + Defines text for the parent control based on an optional condition. + 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). + 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. + + + + + + + + Text for the parent control. + + + + + + The condition that determines when the parent control will use this text. + + + + + + + + + + + + Defines text for the parent control's tooltip. + + + + + + + + Text for the parent control's tooltip. + + + + + + + + + + Defines a treeview. + + + + + + Specifies whether the row always appears selected even when the treeview has lost focus. + + + + + Specifies whether drag and drop is enabled for the treeview. + + + + + Specifies whether an entire row is selected for the treeview. + + + + + Specifies whether the treeview will show buttons. + + + + + Specifies whether lines appear for all treeview items. + + + + + Specifies whether the root nodes have lines beside them. + + + + + + + + A column of a list. + + + + + + + Text for the column header. + Mutually exclusive with the StringId attribute. + + + + + Width of the column. + + + + + + Whether or not this column can grow to fill available width of the listview. + More than one column can be marked with yes - all expandable columns will share available extra space. + This is especially useful if the Window/@AutoResize is yes. + + + + + + + Identifier that references a string resource in the module to define the text for the column header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Optional name for the control. + + + + + Set to 'yes' to disable automatic variable getting and setting, EnableCondition, VisibleCondition, and conditional Text elements. The default is 'no'. + + + + + A condition that determines if the control is enabled. If this condition is true or omitted, then the control will be enabled. + + + + + Height of the control. Non-positive values extend the control to the bottom of the window minus the value. + + + + + Hexadecimal window style for the control. + + + + + Specifies whether the control should be hidden when disabled. + + + + + Specifies whether the control is part of the tab sequence of controls. + + + + + Specifies whether the control is initially visible. + + + + + + A condition that determines if the control is visible. If this condition is true or omitted, then the control will be visible. + + + + + + Width of the control. Non-positive values extend the control to the right of the window minus the value. + + + + + 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. + + + + + 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. + + + + + + + Values of this type will either be "yes" or "no". + + + + + + + + + + + Indicates a system color for a font. + + + + + + + + + + + + + + + + + + Indicates the foreground or background color of a font. + + + + + -- cgit v1.2.3-55-g6feb From 420ce71170489840376c8236da268a7747bb607b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 3 Sep 2017 13:25:34 -0700 Subject: Enable multi-target build and dutil.nupkg --- appveyor.cmd | 13 +++++ appveyor.yml | 9 ++-- src/Cpp.Build.props | 95 +++++++++++++++++++++++++++++++++- src/NativeMultiTargeting.Build.props | 10 ++++ src/dutil/build/WixToolset.DUtil.props | 23 ++++++++ src/dutil/dutil.nuspec | 23 ++++++++ src/dutil/dutil.vcxproj | 45 ++++++++-------- src/dutil/dutil.vcxproj.filters | 1 + src/dutil/packages.config | 4 ++ 9 files changed, 196 insertions(+), 27 deletions(-) create mode 100644 appveyor.cmd create mode 100644 src/NativeMultiTargeting.Build.props create mode 100644 src/dutil/build/WixToolset.DUtil.props create mode 100644 src/dutil/dutil.nuspec create mode 100644 src/dutil/packages.config diff --git a/appveyor.cmd b/appveyor.cmd new file mode 100644 index 00000000..70c4064c --- /dev/null +++ b/appveyor.cmd @@ -0,0 +1,13 @@ +@setlocal +@pushd %~dp0 + +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140_xp +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140_xp + +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141_xp +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141_xp + +msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj + +@popd +@endlocal \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index fc70dbef..432653c6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,15 +2,18 @@ image: Visual Studio 2017 version: 0.0.0.{build} configuration: Release -platform: - - x86 - - x64 environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: 1 NUGET_XMLDOC_MODE: skip +before_build: + - nuget restore + +build_script: + - appveyor.cmd + pull_requests: do_not_increment_build_number: true diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 4fa2f590..1e4d4cbc 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -3,7 +3,98 @@ - $(BaseIntermediateOutputPath) - $(OutputPath) + $(OutputPath) + $(BaseIntermediateOutputPath)$(Platform)\ + $(OutputPath)$(Platform)\ + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + diff --git a/src/NativeMultiTargeting.Build.props b/src/NativeMultiTargeting.Build.props new file mode 100644 index 00000000..98b1933e --- /dev/null +++ b/src/NativeMultiTargeting.Build.props @@ -0,0 +1,10 @@ + + + + + + + $(BaseIntermediateOutputPath)$(PlatformToolset)\$(PlatformTarget)\ + $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ + + diff --git a/src/dutil/build/WixToolset.DUtil.props b/src/dutil/build/WixToolset.DUtil.props new file mode 100644 index 00000000..ec1ae2a4 --- /dev/null +++ b/src/dutil/build/WixToolset.DUtil.props @@ -0,0 +1,23 @@ + + + + + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + + + $(MSBuildThisFileDirectory)native\lib\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + + + $(MSBuildThisFileDirectory)native\lib\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec new file mode 100644 index 00000000..337d43cc --- /dev/null +++ b/src/dutil/dutil.nuspec @@ -0,0 +1,23 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + https://github.com/wixtoolset/dutil/blob/master/LICENSE.TXT + https://github.com/wixtoolset/dutil + false + $description$ + $copyright$ + + + + + + + + + + + diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 7bd78f1e..38608953 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -19,22 +19,6 @@ Release x64 - - Debug - Itanium - - - Release - Itanium - - - Debug - ARM - - - Release - ARM - @@ -44,10 +28,11 @@ true v141_xp MultiByte + WiX Toolset native library foundation - + + + + @@ -186,8 +176,7 @@ Create - - 4091 + 4091;4458 @@ -293,8 +282,20 @@ + + + + + - + + + 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}. + + + + \ No newline at end of file diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters index 644f45a8..b7fda8d4 100644 --- a/src/dutil/dutil.vcxproj.filters +++ b/src/dutil/dutil.vcxproj.filters @@ -361,5 +361,6 @@ Header Files + \ No newline at end of file diff --git a/src/dutil/packages.config b/src/dutil/packages.config new file mode 100644 index 00000000..191b1dee --- /dev/null +++ b/src/dutil/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file -- cgit v1.2.3-55-g6feb From f35f0d078762a48687b29ac224278a7defd5325a Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 17 Sep 2017 15:20:50 -0700 Subject: Update to latest GitVersioning --- src/dutil/dutil.vcxproj | 4 ++-- src/dutil/packages.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 38608953..46002b82 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -53,7 +53,7 @@ - + @@ -296,6 +296,6 @@ 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}. - + \ No newline at end of file diff --git a/src/dutil/packages.config b/src/dutil/packages.config index 191b1dee..764eba29 100644 --- a/src/dutil/packages.config +++ b/src/dutil/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 851e838f9ee1a0388d088eac266655df2fc6d95d Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 17 Sep 2017 15:21:10 -0700 Subject: Remove "lib" from path in NuGet package --- src/dutil/build/WixToolset.DUtil.props | 4 ++-- src/dutil/dutil.nuspec | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dutil/build/WixToolset.DUtil.props b/src/dutil/build/WixToolset.DUtil.props index ec1ae2a4..e39d9cea 100644 --- a/src/dutil/build/WixToolset.DUtil.props +++ b/src/dutil/build/WixToolset.DUtil.props @@ -12,12 +12,12 @@ - $(MSBuildThisFileDirectory)native\lib\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + $(MSBuildThisFileDirectory)native\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) - $(MSBuildThisFileDirectory)native\lib\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + $(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec index 337d43cc..fd40b1d9 100644 --- a/src/dutil/dutil.nuspec +++ b/src/dutil/dutil.nuspec @@ -15,9 +15,9 @@ - - - - + + + + -- cgit v1.2.3-55-g6feb From 8e8b7344cee20ca29aee9105beebc63d35551f1b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 17 Sep 2017 15:22:05 -0700 Subject: Standardize on appveyor.cmd for builds --- appveyor.cmd | 2 ++ appveyor.yml | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 70c4064c..0eab71ad 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -1,6 +1,8 @@ @setlocal @pushd %~dp0 +nuget restore + msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140_xp msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140_xp diff --git a/appveyor.yml b/appveyor.yml index 432653c6..d9da1df5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,9 +8,6 @@ environment: DOTNET_CLI_TELEMETRY_OPTOUT: 1 NUGET_XMLDOC_MODE: skip -before_build: - - nuget restore - build_script: - appveyor.cmd -- cgit v1.2.3-55-g6feb From d94c9611f91ac1018f309afea55c90394907690b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 27 Dec 2017 14:14:37 -0800 Subject: Send build notifications to Slack --- appveyor.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index d9da1df5..0c74d54b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,3 +22,8 @@ skip_tags: true artifacts: - path: build\Release\**\*.nupkg name: nuget + +notifications: +- provider: Slack + incoming_webhook: + secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= -- cgit v1.2.3-55-g6feb From 84d8f809e829f0696006f07997b97f3b6030fcc1 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 23 Jul 2018 21:38:04 -0700 Subject: Ensure log enabled as soon as handle open --- src/dutil/logutil.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp index c471e002..438cdbb8 100644 --- a/src/dutil/logutil.cpp +++ b/src/dutil/logutil.cpp @@ -135,6 +135,8 @@ extern "C" HRESULT DAPI LogOpen( } } + LogUtil_fDisabled = FALSE; + if (fHeader) { LogHeader(); @@ -153,8 +155,6 @@ extern "C" HRESULT DAPI LogOpen( ExitOnFailure(hr, "Failed to copy log path."); } - LogUtil_fDisabled = FALSE; - LExit: if (fEnteredCriticalSection) { -- cgit v1.2.3-55-g6feb From 128c46049fb73f18edd248d2a6e80a933cbe2d80 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 23 Jul 2018 21:38:19 -0700 Subject: Add support for REG_EXPAND_SZ --- src/dutil/regutil.cpp | 87 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp index ec370f58..1b13af81 100644 --- a/src/dutil/regutil.cpp +++ b/src/dutil/regutil.cpp @@ -17,6 +17,13 @@ static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; static HMODULE vhAdvApi32Dll = NULL; static BOOL vfRegInitialized = FALSE; +static HRESULT WriteStringToRegistry( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue, + __in DWORD dwType +); + /******************************************************************** RegInitialize - initializes regutil @@ -221,7 +228,7 @@ extern "C" HRESULT DAPI RegDelete( while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) { ExitOnFailure(hr, "Failed to enumerate key 0"); - + hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); ExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); @@ -733,6 +740,21 @@ LExit: } +/******************************************************************** +RegWriteExpandString - writes a registry key value as an expand string. + +Note: if wzValue is NULL the value will be removed. +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteExpandString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue +) +{ + return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ); +} + + /******************************************************************** RegWriteString - writes a registry key value as a string. @@ -744,30 +766,7 @@ extern "C" HRESULT DAPI RegWriteString( __in_z_opt LPCWSTR wzValue ) { - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD cbValue = 0; - - if (wzValue) - { - hr = ::StringCbLengthW(wzValue, DWORD_MAX, reinterpret_cast(&cbValue)); - ExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_SZ, reinterpret_cast(wzValue), cbValue); - ExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); - } - else - { - er = vpfnRegDeleteValueW(hk, wzName); - if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) - { - er = ERROR_SUCCESS; - } - ExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); - } - -LExit: - return hr; + return WriteStringToRegistry(hk, wzName, wzValue, REG_SZ); } @@ -791,7 +790,7 @@ extern "C" HRESULT DAPI RegWriteStringFormatted( va_end(args); ExitOnFailure(hr, "Failed to allocate %ls value.", wzName); - hr = RegWriteString(hk, wzName, sczValue); + hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ); LExit: ReleaseStr(sczValue); @@ -845,7 +844,7 @@ HRESULT DAPI RegWriteStringArray( { hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); ExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); - + dwTemp -= lstrlenW(rgwzValues[i]) + 1; wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; } @@ -924,3 +923,37 @@ extern "C" HRESULT DAPI RegQueryKey( LExit: return hr; } + + +static HRESULT WriteStringToRegistry( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue, + __in DWORD dwType +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD cbValue = 0; + + if (wzValue) + { + hr = ::StringCbLengthW(wzValue, DWORD_MAX, reinterpret_cast(&cbValue)); + ExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); + + er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), cbValue); + ExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); + } + else + { + er = vpfnRegDeleteValueW(hk, wzName); + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); + } + +LExit: + return hr; +} -- cgit v1.2.3-55-g6feb From 49b1c073fd7aac252b636a813611930fc142cdc5 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 2 Oct 2018 15:24:48 -0700 Subject: Standardize on props with NCrunch support --- src/Cpp.Build.props | 4 ++-- src/Directory.Build.props | 14 +++++++++++--- src/NativeMultiTargeting.Build.props | 2 +- src/dutil/dutil.vcxproj | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 1e4d4cbc..296b36ca 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -3,8 +3,8 @@ - $(OutputPath) - $(BaseIntermediateOutputPath)$(Platform)\ + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ $(OutputPath)$(Platform)\ diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 48ba462d..9eacf3f5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,15 +1,23 @@ - + Debug - $(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\ - $(MSBuildThisFileDirectory)..\build\$(Configuration)\ + false + + $(MSBuildProjectName) + $(MSBuildThisFileDirectory)..\build\ + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ WiX Toolset Team WiX Toolset Copyright (c) .NET Foundation and contributors. All rights reserved. + WiX Toolset diff --git a/src/NativeMultiTargeting.Build.props b/src/NativeMultiTargeting.Build.props index 98b1933e..1ff46559 100644 --- a/src/NativeMultiTargeting.Build.props +++ b/src/NativeMultiTargeting.Build.props @@ -4,7 +4,7 @@ - $(BaseIntermediateOutputPath)$(PlatformToolset)\$(PlatformTarget)\ + $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 46002b82..95b90665 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -288,7 +288,7 @@ - + -- cgit v1.2.3-55-g6feb From f429f6c3710a361446c32d54f398c6db5db89620 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 22 Dec 2018 09:00:16 -0800 Subject: Update to latest Home\repo-template --- .editorconfig | 37 ++++++++++++++++++++++++++++++++++ appveyor.yml | 6 ++++++ src/Directory.Build.props | 8 ++------ src/Directory.Build.targets | 48 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 .editorconfig create mode 100644 src/Directory.Build.targets diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..1d72e683 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,37 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig +# then update all of the repos. + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_sort_system_directives_first = true + +[*.cs] +csharp_indent_case_contents = true : error +csharp_indent_switch_labels = true : error +csharp_new_line_before_open_brace = all +csharp_prefer_braces = true : error +csharp_style_expression_bodied_methods = when_on_single_line : suggestion +csharp_style_expression_bodied_constructors = when_on_single_line : suggestion +csharp_style_expression_bodied_operators = when_on_single_line : suggestion +csharp_style_expression_bodied_properties = when_on_single_line : suggestion +csharp_style_expression_bodied_indexers = when_on_single_line : suggestion +csharp_style_expression_bodied_accessors = when_on_single_line : suggestion +csharp_style_var_elsewhere = true : suggestion +csharp_style_var_for_built_in_types = true : suggestion +csharp_style_var_when_type_is_apparent = true : suggestion +dotnet_style_qualification_for_event = true : error +dotnet_style_qualification_for_field = true : error +dotnet_style_qualification_for_method = true : error +dotnet_style_qualification_for_property = true : error + +[*.targets] +indent_size = 2 diff --git a/appveyor.yml b/appveyor.yml index 0c74d54b..d55322da 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,8 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + image: Visual Studio 2017 version: 0.0.0.{build} @@ -17,6 +22,7 @@ pull_requests: nuget: disable_publish_on_pr: true +skip_branch_with_pr: true skip_tags: true artifacts: diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 9eacf3f5..f09c622b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ @@ -10,7 +10,7 @@ false $(MSBuildProjectName) - $(MSBuildThisFileDirectory)..\build\ + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) $(BaseOutputPath)obj\$(ProjectName)\ $(BaseOutputPath)$(Configuration)\ @@ -20,10 +20,6 @@ WiX Toolset - - $(MSBuildThisFileDirectory)..\..\ - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets new file mode 100644 index 00000000..dac7452a --- /dev/null +++ b/src/Directory.Build.targets @@ -0,0 +1,48 @@ + + + + + + + true + $(SolutionPath) + $(NCrunchOriginalSolutionPath) + + + + + + + $([System.IO.File]::ReadAllText($(TheSolutionPath))) + $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) + (?<="[PackageName]", ")(.*)(?=", ") + + + + + + %(Identity) + $(SolutionFileContent.Contains('\%(Identity).csproj')) + + + + + $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) + $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) + + + + + + + + + + + -- cgit v1.2.3-55-g6feb From 8f18a68edca97a944b2fbfeab7a41596b159f146 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 22 Dec 2018 09:55:02 -0800 Subject: Update to latest .gitignore and prep for new NuGet license format --- .gitignore | 72 ++++++++++++++++++++++++++++++++++++++--------- src/Directory.Build.props | 1 + src/dutil/dutil.nuspec | 3 +- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 3c6208a8..3e8a1553 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache @@ -19,16 +20,21 @@ [Rr]eleases/ x64/ x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ -# Visual Studio 2015 cache/options directory +# Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -49,16 +55,21 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ -**/Properties/launchSettings.json +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp @@ -68,6 +79,7 @@ artifacts/ *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log *.vspscc *.vssscc @@ -96,6 +108,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -116,6 +131,10 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + # Visual Studio code coverage results *.coverage *.coveragexml @@ -164,11 +183,11 @@ PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore -**/packages/* +**/[Pp]ackages/* # except build/, which is used as an MSBuild target. -!**/packages/build/ +!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config +#!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -192,7 +211,7 @@ _pkginfo.txt # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -205,9 +224,15 @@ ClientBin/ *.publishsettings orleans.codegen.cs +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ # RIA/Silverlight projects Generated_Code/ @@ -219,6 +244,8 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files *.mdf @@ -229,6 +256,7 @@ UpgradeLog*.htm *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ @@ -240,9 +268,6 @@ FakesAssemblies/ .ntvs_analysis.dat node_modules/ -# Typescript v1 declaration files -typings/ - # Visual Studio 6 build log *.plg @@ -271,8 +296,8 @@ paket-files/ .idea/ *.sln.iml -# CodeRush -.cr/ +# CodeRush personal settings +.cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ @@ -292,4 +317,25 @@ __pycache__/ *.btp.cs *.btm.cs *.odx.cs -*.xsd.cs \ No newline at end of file +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f09c622b..e853e22d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,6 +17,7 @@ WiX Toolset Team WiX Toolset Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL WiX Toolset diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec index fd40b1d9..7cf7483e 100644 --- a/src/dutil/dutil.nuspec +++ b/src/dutil/dutil.nuspec @@ -5,7 +5,8 @@ $version$ $authors$ $authors$ - https://github.com/wixtoolset/dutil/blob/master/LICENSE.TXT + + https://licenses.nuget.org/MS-RL https://github.com/wixtoolset/dutil false $description$ -- cgit v1.2.3-55-g6feb From 88cd76dbbcce061f80e234489bc18450f56a6e77 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Dec 2018 14:33:39 -0600 Subject: Import deputil from old v4 repo. --- src/dutil/deputil.cpp | 688 ++++++++++++++++++++++++++++++++++++++++++++++++ src/dutil/dutil.vcxproj | 2 + src/dutil/inc/deputil.h | 147 +++++++++++ src/dutil/precomp.h | 1 + 4 files changed, 838 insertions(+) create mode 100644 src/dutil/deputil.cpp create mode 100644 src/dutil/inc/deputil.h diff --git a/src/dutil/deputil.cpp b/src/dutil/deputil.cpp new file mode 100644 index 00000000..b2db0dd6 --- /dev/null +++ b/src/dutil/deputil.cpp @@ -0,0 +1,688 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define ARRAY_GROWTH_SIZE 5 + +static LPCWSTR vcszVersionValue = L"Version"; +static LPCWSTR vcszDisplayNameValue = L"DisplayName"; +static LPCWSTR vcszMinVersionValue = L"MinVersion"; +static LPCWSTR vcszMaxVersionValue = L"MaxVersion"; +static LPCWSTR vcszAttributesValue = L"Attributes"; + +enum eRequiresAttributes { RequiresAttributesMinVersionInclusive = 256, RequiresAttributesMaxVersionInclusive = 512 }; + +// We write to Software\Classes explicitly based on the current security context instead of HKCR. +// See http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. +static LPCWSTR vsczRegistryRoot = L"Software\\Classes\\Installer\\Dependencies\\"; +static LPCWSTR vsczRegistryDependents = L"Dependents"; + +static HRESULT AllocDependencyKeyName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczKeyName + ); + +static HRESULT GetDependencyNameFromKey( + __in HKEY hkHive, + __in LPCWSTR wzKey, + __deref_out_z LPWSTR* psczName + ); + +DAPI_(HRESULT) DepGetProviderInformation( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __deref_out_z_opt LPWSTR* psczId, + __deref_out_z_opt LPWSTR* psczName, + __out_opt DWORD64* pqwVersion + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the dependency key. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + + // Get the Id if requested and available. + if (psczId) + { + hr = RegReadString(hkKey, NULL, psczId); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey); + } + + // Get the DisplayName if requested and available. + if (psczName) + { + hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey); + } + + // Get the Version if requested and available. + if (pqwVersion) + { + hr = RegReadVersion(hkKey, vcszVersionValue, pqwVersion); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepCheckDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes, + __in STRINGDICT_HANDLE sdDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + DWORD64 dw64Version = 0; + int cchMinVersion = 0; + DWORD64 dw64MinVersion = 0; + int cchMaxVersion = 0; + DWORD64 dw64MaxVersion = 0; + BOOL fAllowEqual = FALSE; + LPWSTR sczName = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the key. If that fails, add the missing dependency key to the dependency array if it doesn't already exist. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); + + // If there are no registry values, consider the key orphaned and treat it as missing. + hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); + } + } + + // If the key was not found or the Version value was not found, add the missing dependency key to the dependency array. + if (E_FILENOTFOUND == hr) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey); + } + else + { + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, NULL); + ExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + ExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + + // Check MinVersion if provided. + if (wzMinVersion) + { + cchMinVersion = lstrlenW(wzMinVersion); + if (0 < cchMinVersion) + { + hr = FileVersionFromStringEx(wzMinVersion, cchMinVersion, &dw64MinVersion); + ExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); + + fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; + if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey); + } + else + { + hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); + ExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey); + + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); + ExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + ExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + } + } + + // Check MaxVersion if provided. + if (wzMaxVersion) + { + cchMaxVersion = lstrlenW(wzMaxVersion); + if (0 < cchMaxVersion) + { + hr = FileVersionFromStringEx(wzMaxVersion, cchMaxVersion, &dw64MaxVersion); + ExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); + + fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; + if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey); + } + else + { + hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); + ExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey); + + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); + ExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + ExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + } + } + +LExit: + ReleaseStr(sczName); + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepCheckDependents( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __reserved int /*iAttributes*/, + __in C_STRINGDICT_HANDLE sdIgnoredDependents, + __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, + __inout LPUINT pcDependents + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkProviderKey = NULL; + HKEY hkDependentsKey = NULL; + LPWSTR sczDependentKey = NULL; + LPWSTR sczDependentName = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the key. If that fails, the dependency information is corrupt. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkProviderKey); + ExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey); + + // Try to open the dependencies key. If that does not exist, there are no dependents. + hr = RegOpen(hkProviderKey, vsczRegistryDependents, KEY_READ, &hkDependentsKey); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + + // Now enumerate the dependent keys. If they are not defined in the ignored list, add them to the array. + for (DWORD dwIndex = 0; ; ++dwIndex) + { + hr = RegKeyEnum(hkDependentsKey, dwIndex, &sczDependentKey); + if (E_NOMOREITEMS != hr) + { + ExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey); + } + else + { + hr = S_OK; + break; + } + + // If the key isn't ignored, add it to the dependent array. + hr = DictKeyExists(sdIgnoredDependents, sczDependentKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + else + { + // Get the name of the dependent from the key. + hr = GetDependencyNameFromKey(hkHive, sczDependentKey, &sczDependentName); + ExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey); + + hr = DepDependencyArrayAlloc(prgDependents, pcDependents, sczDependentKey, sczDependentName); + ExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey); + } + } + +LExit: + ReleaseStr(sczDependentName); + ReleaseStr(sczDependentKey); + ReleaseRegKey(hkDependentsKey); + ReleaseRegKey(hkProviderKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepRegisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z LPCWSTR wzVersion, + __in_z LPCWSTR wzDisplayName, + __in_z_opt LPCWSTR wzId, + __in int iAttributes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + BOOL fCreated = FALSE; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Create the dependency key (or open it if it already exists). + hr = RegCreateEx(hkHive, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); + ExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey); + + // Set the id if it was provided. + if (wzId) + { + hr = RegWriteString(hkKey, NULL, wzId); + ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId); + } + + // Set the version. + hr = RegWriteString(hkKey, vcszVersionValue, wzVersion); + ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion); + + // Set the display name. + hr = RegWriteString(hkKey, vcszDisplayNameValue, wzDisplayName); + ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName); + + // Set the attributes if non-zero. + if (0 != iAttributes) + { + hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); + ExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepDependentExists( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDependentKey = NULL; + HKEY hkDependentKey = NULL; + + // Format the provider dependents registry key. + hr = StrAllocFormatted(&sczDependentKey, L"%ls%ls\\%ls\\%ls", vsczRegistryRoot, wzDependencyProviderKey, vsczRegistryDependents, wzProviderKey); + ExitOnFailure(hr, "Failed to format registry key to dependent."); + + hr = RegOpen(hkHive, sczDependentKey, KEY_READ, &hkDependentKey); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey); + } + +LExit: + ReleaseRegKey(hkDependentKey); + ReleaseStr(sczDependentKey); + + return hr; +} + +DAPI_(HRESULT) DepRegisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDependencyKey = NULL; + HKEY hkDependencyKey = NULL; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + BOOL fCreated = FALSE; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzDependencyProviderKey, &sczDependencyKey); + ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey); + + // Create the dependency key (or open it if it already exists). + hr = RegCreateEx(hkHive, sczDependencyKey, KEY_WRITE, FALSE, NULL, &hkDependencyKey, &fCreated); + ExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey); + + // Create the subkey to register the dependent. + hr = StrAllocFormatted(&sczKey, L"%ls\\%ls", vsczRegistryDependents, wzProviderKey); + ExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + + hr = RegCreateEx(hkDependencyKey, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); + ExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey); + + // Set the minimum version if not NULL. + hr = RegWriteString(hkKey, vcszMinVersionValue, wzMinVersion); + ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion); + + // Set the maximum version if not NULL. + hr = RegWriteString(hkKey, vcszMaxVersionValue, wzMaxVersion); + ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion); + + // Set the attributes if non-zero. + if (0 != iAttributes) + { + hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); + ExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + ReleaseRegKey(hkDependencyKey); + ReleaseStr(sczDependencyKey); + + return hr; +} + +DAPI_(HRESULT) DepUnregisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Delete the entire key including all sub-keys. + hr = RegDelete(hkHive, sczKey, REG_KEY_DEFAULT, TRUE); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepUnregisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistryRoot = NULL; + HKEY hkDependencyProviderKey = NULL; + HKEY hkRegistryDependents = NULL; + DWORD cSubKeys = 0; + DWORD cValues = 0; + + // Open the root key. We may delete the wzDependencyProviderKey during clean up. + hr = RegOpen(hkHive, vsczRegistryRoot, KEY_READ, &hkRegistryRoot); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot); + } + else + { + ExitFunction(); + } + + // Try to open the dependency key. If that does not exist, simply return. + hr = RegOpen(hkRegistryRoot, wzDependencyProviderKey, KEY_READ, &hkDependencyProviderKey); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey); + } + else + { + ExitFunction(); + } + + // Try to open the dependents subkey to enumerate. + hr = RegOpen(hkDependencyProviderKey, vsczRegistryDependents, KEY_READ, &hkRegistryDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + } + else + { + ExitFunction(); + } + + // Delete the wzProviderKey dependent sub-key. + hr = RegDelete(hkRegistryDependents, wzProviderKey, REG_KEY_DEFAULT, TRUE); + ExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + + // If there are no remaining dependents, delete the Dependents subkey. + hr = RegQueryKey(hkRegistryDependents, &cSubKeys, NULL); + ExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey); + + if (0 < cSubKeys) + { + ExitFunction(); + } + + // Release the handle to make sure it's deleted immediately. + ReleaseRegKey(hkRegistryDependents); + + // Fail if there are any subkeys since we just checked. + hr = RegDelete(hkDependencyProviderKey, vsczRegistryDependents, REG_KEY_DEFAULT, FALSE); + ExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + + // If there are no values, delete the provider dependency key. + hr = RegQueryKey(hkDependencyProviderKey, NULL, &cValues); + ExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey); + + if (0 == cValues) + { + // Release the handle to make sure it's deleted immediately. + ReleaseRegKey(hkDependencyProviderKey); + + // Fail if there are any subkeys since we just checked. + hr = RegDelete(hkRegistryRoot, wzDependencyProviderKey, REG_KEY_DEFAULT, FALSE); + ExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey); + } + +LExit: + ReleaseRegKey(hkRegistryDependents); + ReleaseRegKey(hkDependencyProviderKey); + ReleaseRegKey(hkRegistryRoot); + + return hr; +} + +DAPI_(HRESULT) DepDependencyArrayAlloc( + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __in_z LPCWSTR wzKey, + __in_z_opt LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + UINT cRequired = 0; + DEPENDENCY* pDependency = NULL; + + hr = ::UIntAdd(*pcDependencies, 1, &cRequired); + ExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array."); + + hr = MemEnsureArraySize(reinterpret_cast(prgDependencies), cRequired, sizeof(DEPENDENCY), ARRAY_GROWTH_SIZE); + ExitOnFailure(hr, "Failed to allocate memory for the dependency array."); + + pDependency = static_cast(&(*prgDependencies)[*pcDependencies]); + ExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid."); + + hr = StrAllocString(&(pDependency->sczKey), wzKey, 0); + ExitOnFailure(hr, "Failed to allocate the string key in the dependency array."); + + if (wzName) + { + hr = StrAllocString(&(pDependency->sczName), wzName, 0); + ExitOnFailure(hr, "Failed to allocate the string name in the dependency array."); + } + + // Update the number of current elements in the dependency array. + *pcDependencies = cRequired; + +LExit: + return hr; +} + +DAPI_(void) DepDependencyArrayFree( + __in_ecount(cDependencies) DEPENDENCY* rgDependencies, + __in UINT cDependencies + ) +{ + for (UINT i = 0; i < cDependencies; ++i) + { + ReleaseStr(rgDependencies[i].sczKey); + ReleaseStr(rgDependencies[i].sczName); + } + + ReleaseMem(rgDependencies); +} + +/*************************************************************************** + AllocDependencyKeyName - Allocates and formats the root registry key name. + +***************************************************************************/ +static HRESULT AllocDependencyKeyName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczKeyName + ) +{ + HRESULT hr = S_OK; + size_t cchName = 0; + size_t cchKeyName = 0; + + // Get the length of the static registry root once. + static size_t cchRegistryRoot = ::lstrlenW(vsczRegistryRoot); + + // Get the length of the dependency, and add to the length of the root. + hr = ::StringCchLengthW(wzName, STRSAFE_MAX_CCH, &cchName); + ExitOnFailure(hr, "Failed to get string length of dependency name."); + + // Add the sizes together to allocate memory once (callee will add space for nul). + hr = ::SizeTAdd(cchRegistryRoot, cchName, &cchKeyName); + ExitOnFailure(hr, "Failed to add the string lengths together."); + + // Allocate and concat the strings together. + hr = StrAllocString(psczKeyName, vsczRegistryRoot, cchKeyName); + ExitOnFailure(hr, "Failed to allocate string for dependency registry root."); + + hr = StrAllocConcat(psczKeyName, wzName, cchName); + ExitOnFailure(hr, "Failed to concatenate the dependency key name."); + +LExit: + return hr; +} + +/*************************************************************************** + GetDependencyNameFromKey - Attempts to name of the dependency from the key. + +***************************************************************************/ +static HRESULT GetDependencyNameFromKey( + __in HKEY hkHive, + __in LPCWSTR wzProviderKey, + __deref_out_z LPWSTR* psczName + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the dependency key. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + + // Get the DisplayName if available. + hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 95b90665..606eb87b 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -171,6 +171,7 @@ + @@ -238,6 +239,7 @@ + diff --git a/src/dutil/inc/deputil.h b/src/dutil/inc/deputil.h new file mode 100644 index 00000000..a08d2eb5 --- /dev/null +++ b/src/dutil/inc/deputil.h @@ -0,0 +1,147 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); } +#define ReleaseNullDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); rg = NULL; } + +typedef struct _DEPENDENCY +{ + LPWSTR sczKey; + LPWSTR sczName; +} DEPENDENCY; + + +/*************************************************************************** + DepGetProviderInformation - gets the various pieces of data registered + with a dependency. + + Note: Returns E_NOTFOUND if the dependency was not found. +***************************************************************************/ +DAPI_(HRESULT) DepGetProviderInformation( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __deref_out_z_opt LPWSTR* psczId, + __deref_out_z_opt LPWSTR* psczName, + __out_opt DWORD64* pqwVersion + ); + +/*************************************************************************** + DepCheckDependency - Checks that the dependency is registered and within + the proper version range. + + Note: Returns E_NOTFOUND if the dependency was not found. +***************************************************************************/ +DAPI_(HRESULT) DepCheckDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes, + __in STRINGDICT_HANDLE sdDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ); + +/*************************************************************************** + DepCheckDependents - Checks if any dependents are still installed for the + given provider key. + +***************************************************************************/ +DAPI_(HRESULT) DepCheckDependents( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in int iAttributes, + __in C_STRINGDICT_HANDLE sdIgnoredDependents, + __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, + __inout LPUINT pcDependents + ); + +/*************************************************************************** + DepRegisterDependency - Registers the dependency provider. + +***************************************************************************/ +DAPI_(HRESULT) DepRegisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z LPCWSTR wzVersion, + __in_z LPCWSTR wzDisplayName, + __in_z_opt LPCWSTR wzId, + __in int iAttributes + ); + +/*************************************************************************** + DepDependentExists - Determines if a dependent is registered. + + Note: Returns S_OK if dependent is registered. + Returns E_FILENOTFOUND if dependent is not registered +***************************************************************************/ +DAPI_(HRESULT) DepDependentExists( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DepRegisterDependent - Registers a dependent under the dependency provider. + +***************************************************************************/ +DAPI_(HRESULT) DepRegisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes + ); + +/*************************************************************************** + DepUnregisterDependency - Removes the dependency provider. + + Note: Caller should call CheckDependents prior to remove a dependency. + Returns E_FILENOTFOUND if the dependency is not registered. +***************************************************************************/ +DAPI_(HRESULT) DepUnregisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DepUnregisterDependent - Removes a dependent under the dependency provider. + + Note: Returns E_FILENOTFOUND if neither the dependency or dependent are + registered. + ***************************************************************************/ +DAPI_(HRESULT) DepUnregisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DependencyArrayAlloc - Allocates or expands an array of DEPENDENCY structs. + +***************************************************************************/ +DAPI_(HRESULT) DepDependencyArrayAlloc( + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __in_z LPCWSTR wzKey, + __in_z_opt LPCWSTR wzName + ); + +/*************************************************************************** + DepDependencyArrayFree - Frees an array of DEPENDENCY structs. + +***************************************************************************/ +DAPI_(void) DepDependencyArrayFree( + __in_ecount(cDependencies) DEPENDENCY* rgDependencies, + __in UINT cDependencies + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h index 3acbca43..c305573d 100644 --- a/src/dutil/precomp.h +++ b/src/dutil/precomp.h @@ -56,6 +56,7 @@ #include "guidutil.h" #include "gdiputil.h" #include "dictutil.h" +#include "deputil.h" // NOTE: This must come after dictutil.h since it uses it. #include "inetutil.h" #include "iniutil.h" #include "jsonutil.h" -- cgit v1.2.3-55-g6feb From c55c1f2993d4ec950fc6b04508bc2b03cd733035 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 6 Jan 2019 21:09:03 -0600 Subject: Undo changes for condutil/varutil. --- src/dutil/condutil.cpp | 20 --- src/dutil/dutil.vcxproj | 4 - src/dutil/dutil.vcxproj.filters | 12 -- src/dutil/inc/condutil.h | 19 --- src/dutil/inc/varutil.h | 126 ------------------ src/dutil/precomp.h | 2 - src/dutil/varutil.cpp | 274 ---------------------------------------- 7 files changed, 457 deletions(-) delete mode 100644 src/dutil/condutil.cpp delete mode 100644 src/dutil/inc/condutil.h delete mode 100644 src/dutil/inc/varutil.h delete mode 100644 src/dutil/varutil.cpp diff --git a/src/dutil/condutil.cpp b/src/dutil/condutil.cpp deleted file mode 100644 index 99923c18..00000000 --- a/src/dutil/condutil.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// function definitions - -/******************************************************************** -CondEvaluate - evaluates the condition using the given variables. -********************************************************************/ -extern "C" HRESULT DAPI CondEvaluate( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzCondition, - __out BOOL* pf - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzCondition); - UNREFERENCED_PARAMETER(pf); - return E_NOTIMPL; -} diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 606eb87b..d8393cb4 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -168,7 +168,6 @@ - @@ -220,7 +219,6 @@ - @@ -236,7 +234,6 @@ - @@ -276,7 +273,6 @@ - diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters index b7fda8d4..ddd008ed 100644 --- a/src/dutil/dutil.vcxproj.filters +++ b/src/dutil/dutil.vcxproj.filters @@ -45,9 +45,6 @@ Source Files - - Source Files - Source Files @@ -162,9 +159,6 @@ Source Files - - Source Files - Source Files @@ -221,9 +215,6 @@ Header Files - - Header Files - Header Files @@ -329,9 +320,6 @@ Header Files - - Header Files - Header Files diff --git a/src/dutil/inc/condutil.h b/src/dutil/inc/condutil.h deleted file mode 100644 index fb960042..00000000 --- a/src/dutil/inc/condutil.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -// function declarations - -HRESULT DAPI CondEvaluate( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzCondition, - __out BOOL* pf - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/dutil/inc/varutil.h b/src/dutil/inc/varutil.h deleted file mode 100644 index 86d0aca0..00000000 --- a/src/dutil/inc/varutil.h +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -#define ReleaseVariables(vh) if (vh) { VarDestroy(vh, NULL); } -#define ReleaseVariableValue(v) if (v) { VarFreeValue(v); } -#define ReleaseNullVariables(vh) if (vh) { VarDestroy(vh, NULL); vh = NULL; } -#define ReleaseNullVariableValue(v) if (v) { VarFreeValue(v); v = NULL; } - -typedef void* VARIABLE_ENUM_HANDLE; -typedef void* VARIABLES_HANDLE; -typedef const void* C_VARIABLES_HANDLE; - -extern const int VARIABLE_ENUM_HANDLE_BYTES; -extern const int VARIABLES_HANDLE_BYTES; - -typedef void(*PFN_FREEVARIABLECONTEXT)( - __in LPVOID pvContext - ); - -typedef enum VARIABLE_VALUE_TYPE -{ - VARIABLE_VALUE_TYPE_NONE, - VARIABLE_VALUE_TYPE_NUMERIC, - VARIABLE_VALUE_TYPE_STRING, - VARIABLE_VALUE_TYPE_VERSION, -} VARIABLE_VALUE_TYPE; - -typedef struct _VARIABLE_VALUE -{ - VARIABLE_VALUE_TYPE type; - union - { - LONGLONG llValue; - DWORD64 qwValue; - LPWSTR sczValue; - }; - BOOL fHidden; - LPVOID pvContext; -} VARIABLE_VALUE; - -HRESULT DAPI VarCreate( - __out_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE* ppVariables - ); -void DAPI VarDestroy( - __in_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE pVariables, - __in_opt PFN_FREEVARIABLECONTEXT vpfFreeVariableContext - ); -void DAPI VarFreeValue( - __in VARIABLE_VALUE* pValue - ); -HRESULT DAPI VarEscapeString( - __in_z LPCWSTR wzIn, - __out_z LPWSTR* psczOut - ); -HRESULT DAPI VarFormatString( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut - ); -HRESULT DAPI VarGetFormatted( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue - ); -HRESULT DAPI VarGetNumeric( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue - ); -HRESULT DAPI VarGetString( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue - ); -HRESULT DAPI VarGetVersion( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in DWORD64* pqwValue - ); -HRESULT DAPI VarGetValue( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out VARIABLE_VALUE** ppValue - ); -HRESULT DAPI VarSetNumeric( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in LONGLONG llValue - ); -HRESULT DAPI VarSetString( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue - ); -HRESULT DAPI VarSetVersion( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in DWORD64 qwValue - ); -HRESULT DAPI VarSetValue( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in VARIABLE_VALUE* pValue - ); -HRESULT DAPI VarStartEnum( - __in VARIABLES_HANDLE pVariables, - __out_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE* ppEnum, - __out VARIABLE_VALUE** ppValue - ); -HRESULT DAPI VarNextVariable( - __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum, - __out VARIABLE_VALUE** ppValue - ); -void DAPI VarFinishEnum( - __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h index c305573d..374d0fc1 100644 --- a/src/dutil/precomp.h +++ b/src/dutil/precomp.h @@ -86,8 +86,6 @@ #include "uncutil.h" #include "uriutil.h" #include "userutil.h" -#include "varutil.h" -#include "condutil.h" // NOTE: This must come after varutil.h since it uses it. #include "wiutil.h" #include "wuautil.h" #include // This header is needed for msxml2.h to compile correctly diff --git a/src/dutil/varutil.cpp b/src/dutil/varutil.cpp deleted file mode 100644 index 88716105..00000000 --- a/src/dutil/varutil.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -struct VARIABLE_ENUM_STRUCT -{ -}; - -struct VARIABLES_STRUCT -{ -}; - -const int VARIABLE_ENUM_HANDLE_BYTES = sizeof(VARIABLE_ENUM_STRUCT); -const int VARIABLES_HANDLE_BYTES = sizeof(VARIABLES_STRUCT); - -// function definitions - -/******************************************************************** -VarCreate - creates a variables group. -********************************************************************/ -extern "C" HRESULT DAPI VarCreate( - __out_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE* ppVariables - ) -{ - UNREFERENCED_PARAMETER(ppVariables); - return E_NOTIMPL; -} - -/******************************************************************** -VarDestroy - destroys a variables group, accepting an optional callback - to help free the variable contexts. -********************************************************************/ -extern "C" void DAPI VarDestroy( - __in_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE pVariables, - __in_opt PFN_FREEVARIABLECONTEXT vpfFreeVariableContext - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(vpfFreeVariableContext); -} - -/******************************************************************** -VarFreeValue - frees a variable value. -********************************************************************/ -extern "C" void DAPI VarFreeValue( - __in VARIABLE_VALUE* pValue - ) -{ - UNREFERENCED_PARAMETER(pValue); -} - -/******************************************************************** -VarEscapeString - escapes special characters in wzIn so that it can - be used in conditions or variable values. -********************************************************************/ -extern "C" HRESULT DAPI VarEscapeString( - __in_z LPCWSTR wzIn, - __out_z LPWSTR* psczOut - ) -{ - UNREFERENCED_PARAMETER(wzIn); - UNREFERENCED_PARAMETER(psczOut); - return E_NOTIMPL; -} - -/******************************************************************** -VarFormatString - similar to MsiFormatRecord. -********************************************************************/ -extern "C" HRESULT DAPI VarFormatString( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzIn); - UNREFERENCED_PARAMETER(psczOut); - UNREFERENCED_PARAMETER(pcchOut); - return E_NOTIMPL; -} - -/******************************************************************** -VarGetFormatted - gets the formatted value of a single variable. -********************************************************************/ -extern "C" HRESULT DAPI VarGetFormatted( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(psczValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarGetNumeric - gets the numeric value of a variable. If the type of - the variable is not numeric, it will attempt to - convert the value into a number. -********************************************************************/ -extern "C" HRESULT DAPI VarGetNumeric( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(pllValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarGetString - gets the unformatted string value of a variable. If - the type of the variable is not string, it will - convert the value to a string. -********************************************************************/ -extern "C" HRESULT DAPI VarGetString( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(psczValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarGetVersion - gets the version value of a variable. If the type of - the variable is not version, it will attempt to - convert the value into a version. -********************************************************************/ -extern "C" HRESULT DAPI VarGetVersion( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in DWORD64* pqwValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(pqwValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarGetValue - gets the value of a variable along with its metadata. -********************************************************************/ -extern "C" HRESULT DAPI VarGetValue( - __in C_VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __out VARIABLE_VALUE** ppValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(ppValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarSetNumeric - sets the value of the variable to a number, the type - of the variable to numeric, and adds the variable to - the group if necessary. -********************************************************************/ -extern "C" HRESULT DAPI VarSetNumeric( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in LONGLONG llValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(llValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarSetString - sets the value of the variable to a string, the type - of the variable to string, and adds the variable to - the group if necessary. -********************************************************************/ -extern "C" HRESULT DAPI VarSetString( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(wzValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarSetVersion - sets the value of the variable to a version, the type - of the variable to version, and adds the variable to - the group if necessary. -********************************************************************/ -extern "C" HRESULT DAPI VarSetVersion( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in DWORD64 qwValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(qwValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarSetValue - sets the value of the variable along with its metadata. - Also adds the variable to the group if necessary. -********************************************************************/ -extern "C" HRESULT DAPI VarSetValue( - __in VARIABLES_HANDLE pVariables, - __in_z LPCWSTR wzVariable, - __in VARIABLE_VALUE* pValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(wzVariable); - UNREFERENCED_PARAMETER(pValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarStartEnum - starts the enumeration of the variable group. There - is no guarantee for the order of the variable enumeration. - -NOTE: caller is responsible for calling VarFinishEnum even if function fails -********************************************************************/ -extern "C" HRESULT DAPI VarStartEnum( - __in VARIABLES_HANDLE pVariables, - __out_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE* ppEnum, - __out VARIABLE_VALUE** ppValue - ) -{ - UNREFERENCED_PARAMETER(pVariables); - UNREFERENCED_PARAMETER(ppEnum); - UNREFERENCED_PARAMETER(ppValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarNextVariable - continues the enumeration of the variable group. It - will fail if any variables were added or removed - during the enumeration. - -NOTE: caller is responsible for calling VarFinishEnum even if function fails -********************************************************************/ -extern "C" HRESULT DAPI VarNextVariable( - __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum, - __out VARIABLE_VALUE** ppValue - ) -{ - UNREFERENCED_PARAMETER(pEnum); - UNREFERENCED_PARAMETER(ppValue); - return E_NOTIMPL; -} - -/******************************************************************** -VarFinishEnum - cleans up resources used for the enumeration. -********************************************************************/ -extern "C" void DAPI VarFinishEnum( - __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum - ) -{ - UNREFERENCED_PARAMETER(pEnum); -} -- cgit v1.2.3-55-g6feb From f8c806374a2fdbf8fb85a654d561e37a419f5ea3 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 13 Jan 2019 18:51:12 -0600 Subject: Add ARM and ARM64 for v141. --- appveyor.cmd | 2 + dutil.sln | 12 ++++ src/Cpp.Build.props | 6 +- src/dutil/dutil.nuspec | 2 + src/dutil/dutil.vcxproj | 134 +++++----------------------------------- src/dutil/dutil.vcxproj.filters | 12 ++-- 6 files changed, 44 insertions(+), 124 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 0eab71ad..e07239f7 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -8,6 +8,8 @@ msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140_xp msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141_xp msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141_xp +msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 +msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj diff --git a/dutil.sln b/dutil.sln index 5f83f097..ade64266 100644 --- a/dutil.sln +++ b/dutil.sln @@ -7,16 +7,28 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vc EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM.ActiveCfg = Debug|ARM + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM.Build.0 = Debug|ARM + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.Build.0 = Debug|ARM64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM.ActiveCfg = Release|ARM + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM.Build.0 = Release|ARM + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.ActiveCfg = Release|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.Build.0 = Release|ARM64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32 diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 296b36ca..0e00132b 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -8,6 +8,10 @@ $(OutputPath)$(Platform)\ + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + $(DisableSpecificCompilerWarnings) @@ -16,7 +20,7 @@ WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) Use precomp.h - StdCall + StdCall true false -YlprecompDefine diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec index 7cf7483e..f74081be 100644 --- a/src/dutil/dutil.nuspec +++ b/src/dutil/dutil.nuspec @@ -20,5 +20,7 @@ + + diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index d8393cb4..f44b195c 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -3,10 +3,26 @@ + + Debug + ARM + + + Debug + ARM64 + Debug Win32 + + Release + ARM + + + Release + ARM64 + Release Win32 @@ -27,135 +43,19 @@ dutil true v141_xp + v141 MultiByte WiX Toolset native library foundation - - - - diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters index ddd008ed..acf7591c 100644 --- a/src/dutil/dutil.vcxproj.filters +++ b/src/dutil/dutil.vcxproj.filters @@ -132,9 +132,6 @@ Source Files - - Source Files - Source Files @@ -186,6 +183,9 @@ Source Files + + Source Files + @@ -311,9 +311,6 @@ Header Files - - Header Files - Header Files @@ -344,6 +341,9 @@ Header Files + + Header Files + -- cgit v1.2.3-55-g6feb From 570c113409a6aaab462efec8e223e75a89e4ded0 Mon Sep 17 00:00:00 2001 From: Jacob Hoover Date: Thu, 31 Jan 2019 11:12:42 -0600 Subject: WIXBUG5809 - jchoover - FileNames may be case sensitive when using LSW, so preserve the case of the file name and make the dictionary case insensitive. --- src/dutil/cabcutil.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/dutil/cabcutil.cpp b/src/dutil/cabcutil.cpp index 35fefaba..8619822d 100644 --- a/src/dutil/cabcutil.cpp +++ b/src/dutil/cabcutil.cpp @@ -302,7 +302,7 @@ extern "C" HRESULT DAPI CabCBegin( // case is we'll leave a zero byte file behind in the temp folder. 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); - hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_NONE); + hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); ExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files @@ -383,16 +383,9 @@ extern "C" HRESULT DAPI CabCAddFile( HRESULT hr = S_OK; CABC_DATA *pcd = reinterpret_cast(hContext); CABC_FILE *pcfDuplicate = NULL; - LPWSTR sczUpperCaseFile = NULL; LONGLONG llFileSize = 0; PMSIFILEHASHINFO pmfLocalHash = pmfHash; - hr = StrAllocString(&sczUpperCaseFile, wzFile, 0); - ExitOnFailure(hr, "Failed to allocate new string for file %ls", wzFile); - - // Modifies the string in-place - StrStringToUpper(sczUpperCaseFile); - // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled if(!pcd->fCabinetSplittingEnabled) @@ -401,7 +394,7 @@ extern "C" HRESULT DAPI CabCAddFile( hr = FileSize(wzFile, &llFileSize); ExitOnFailure(hr, "Failed to check size of file %ls", wzFile); - hr = CheckForDuplicateFile(pcd, &pcfDuplicate, sczUpperCaseFile, &pmfLocalHash, llFileSize); + hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize); ExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); } @@ -411,20 +404,18 @@ extern "C" HRESULT DAPI CabCAddFile( hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index); ExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); - hr = AddDuplicateFile(pcd, index, sczUpperCaseFile, wzToken, pcd->dwLastFileIndex); + hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex); ExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); } else { - hr = AddNonDuplicateFile(pcd, sczUpperCaseFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); + hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); ExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); } ++pcd->dwLastFileIndex; LExit: - ReleaseStr(sczUpperCaseFile); - // If we allocated a hash struct ourselves, free it if (pmfHash != pmfLocalHash) { -- cgit v1.2.3-55-g6feb From 1ec68f7c9925536e9f11f5f849eb812a99dfa8ce Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 9 May 2019 15:12:54 -0700 Subject: Support VS2019 --- appveyor.cmd | 9 +++++++-- appveyor.yml | 9 ++++++++- src/dutil/build/WixToolset.DUtil.props | 5 +++++ src/dutil/dutil.nuspec | 8 ++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index e07239f7..7b8aeb28 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -8,8 +8,13 @@ msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140_xp msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141_xp msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141_xp -msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 -msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 +rem msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 +rem msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 + +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 +rem msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v142 +rem msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj diff --git a/appveyor.yml b/appveyor.yml index d55322da..6df525ad 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,12 @@ # Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml # then update all of the repos. -image: Visual Studio 2017 +branches: + only: + - master + - develop + +image: Visual Studio 2019 Preview version: 0.0.0.{build} configuration: Release @@ -28,6 +33,8 @@ skip_tags: true artifacts: - path: build\Release\**\*.nupkg name: nuget +- path: build\Release\**\*.msi + name: msi notifications: - provider: Slack diff --git a/src/dutil/build/WixToolset.DUtil.props b/src/dutil/build/WixToolset.DUtil.props index e39d9cea..35749be8 100644 --- a/src/dutil/build/WixToolset.DUtil.props +++ b/src/dutil/build/WixToolset.DUtil.props @@ -20,4 +20,9 @@ $(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + $(MSBuildThisFileDirectory)native\v142\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec index f74081be..ab74dc79 100644 --- a/src/dutil/dutil.nuspec +++ b/src/dutil/dutil.nuspec @@ -20,7 +20,15 @@ + + + + -- cgit v1.2.3-55-g6feb From a4d49f0a2a8b59ae4fdfa02081c522bc0d02d21a Mon Sep 17 00:00:00 2001 From: Jacob Hoover Date: Thu, 29 Aug 2019 12:13:16 -0500 Subject: jchoover: WIXBUG:6071 - Enable HTTP to HTTPS redirects for burn downloads. --- src/dutil/dlutil.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dutil/dlutil.cpp b/src/dutil/dlutil.cpp index 81455df0..1b30f410 100644 --- a/src/dutil/dlutil.cpp +++ b/src/dutil/dlutil.cpp @@ -558,7 +558,11 @@ static HRESULT OpenRequest( { dwRequestFlags |= INTERNET_FLAG_SECURE; } - + else if (INTERNET_SCHEME_HTTP == scheme) + { + dwRequestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; + } + // Allocate the resource name. hr = StrAllocString(&sczResource, wzResource, 0); ExitOnFailure(hr, "Failed to allocate string for resource URI."); -- cgit v1.2.3-55-g6feb From 00fe99e60cabf3200a316498ee9c9b90060ec937 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sat, 9 May 2020 22:24:49 -0400 Subject: Drop XP support and enable ARM64. --- appveyor.cmd | 16 ++++++++-------- appveyor.yml | 2 +- src/Cpp.Build.props | 2 +- src/dutil/dutil.nuspec | 13 +++++-------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 7b8aeb28..49869ace 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -3,18 +3,18 @@ nuget restore -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140_xp -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140_xp +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140 +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141_xp -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141_xp -rem msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 -rem msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141 +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141 +REM msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 +REM msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 -rem msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v142 -rem msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 +msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v142 +msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj diff --git a/appveyor.yml b/appveyor.yml index 6df525ad..522e5af3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ branches: - master - develop -image: Visual Studio 2019 Preview +image: Visual Studio 2019 version: 0.0.0.{build} configuration: Release diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 0e00132b..44a042c7 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -8,7 +8,7 @@ $(OutputPath)$(Platform)\ - + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec index ab74dc79..2ae420e1 100644 --- a/src/dutil/dutil.nuspec +++ b/src/dutil/dutil.nuspec @@ -5,8 +5,7 @@ $version$ $authors$ $authors$ - - https://licenses.nuget.org/MS-RL + MS-RL https://github.com/wixtoolset/dutil false $description$ @@ -16,19 +15,17 @@ - - - - + + + + - -- cgit v1.2.3-55-g6feb From 05781eabd0e71261959aabe27ab5ff2d3d859ca2 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 14 May 2020 18:23:05 +1000 Subject: WIXFEAT:6164 Add WiuInitializeInternalUI. --- src/dutil/inc/wiutil.h | 7 ++++++- src/dutil/wiutil.cpp | 35 ++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/dutil/inc/wiutil.h b/src/dutil/inc/wiutil.h index 4264b815..07f6b56c 100644 --- a/src/dutil/inc/wiutil.h +++ b/src/dutil/inc/wiutil.h @@ -330,10 +330,15 @@ HRESULT DAPI WiuEnableLog( __in_z LPCWSTR wzLogFile, __in DWORD dwLogAttributes ); +HRESULT DAPI WiuInitializeInternalUI( + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); HRESULT DAPI WiuInitializeExternalUI( __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, __in INSTALLUILEVEL internalUILevel, - __in HWND hwndParent, + __in_opt HWND hwndParent, __in LPVOID pvContext, __in BOOL fRollback, __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp index 1a489d54..1b3dd317 100644 --- a/src/dutil/wiutil.cpp +++ b/src/dutil/wiutil.cpp @@ -715,10 +715,32 @@ LExit: } +extern "C" HRESULT DAPI WiuInitializeInternalUI( + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + HRESULT hr = S_OK; + + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); + + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); + pExecuteContext->hwndPreviousParentWindow = hwndParent; + + if (INSTALLUILEVEL_NOCHANGE == pExecuteContext->previousInstallUILevel) + { + hr = E_INVALIDARG; + } + + return hr; +} + + extern "C" HRESULT DAPI WiuInitializeExternalUI( __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, __in INSTALLUILEVEL internalUILevel, - __in HWND hwndParent, + __in_opt HWND hwndParent, __in LPVOID pvContext, __in BOOL fRollback, __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext @@ -730,7 +752,7 @@ extern "C" HRESULT DAPI WiuInitializeExternalUI( DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | - INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA| + INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE; if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor) @@ -738,15 +760,14 @@ extern "C" HRESULT DAPI WiuInitializeExternalUI( dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE; } - memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); + // Wire the internal and external UI handler. + hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext); + ExitOnFailure(hr, "Failed to set internal UI level and window."); + pExecuteContext->fRollback = fRollback; pExecuteContext->pfnMessageHandler = pfnMessageHandler; pExecuteContext->pvContext = pvContext; - // Wire the internal and external UI handler. - pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); - pExecuteContext->hwndPreviousParentWindow = hwndParent; - // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external // UI handler if necesary. if (vpfnMsiSetExternalUIRecord) -- cgit v1.2.3-55-g6feb From 94f9303dabb5d28884ac05b179b004f37eae89eb Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 14 May 2020 18:31:02 +1000 Subject: Add StrAllocConcatFormattedSecure. --- src/dutil/inc/strutil.h | 5 +++++ src/dutil/strutil.cpp | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h index 1a2ed1d8..c73615aa 100644 --- a/src/dutil/inc/strutil.h +++ b/src/dutil/inc/strutil.h @@ -100,6 +100,11 @@ HRESULT __cdecl StrAllocConcatFormatted( __in __format_string LPCWSTR wzFormat, ... ); +HRESULT __cdecl StrAllocConcatFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); HRESULT __cdecl StrAllocFormattedSecure( __deref_out_z LPWSTR* ppwz, __in __format_string LPCWSTR wzFormat, diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp index 2e5e2f96..4e184c34 100644 --- a/src/dutil/strutil.cpp +++ b/src/dutil/strutil.cpp @@ -919,9 +919,43 @@ LExit: } +/******************************************************************** +StrAllocConcatFormattedSecure - allocates or reuses dynamic string +memory and adds a formatted string. If the memory needs to be +reallocated, calls SecureZeroMemory on original block of memory after +it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args); + va_end(args); + ExitOnFailure(hr, "Failed to allocate formatted string"); + + hr = StrAllocConcatSecure(ppwz, sczFormatted, 0); + +LExit: + ReleaseStr(sczFormatted); + + return hr; +} + + /******************************************************************** StrAllocFormattedSecure - allocates or reuses dynamic string memory -and formats it. If the memory needs to reallocated, +and formats it. If the memory needs to be reallocated, calls SecureZeroMemory on original block of memory after it is moved. NOTE: caller is responsible for freeing ppwz even if function fails -- cgit v1.2.3-55-g6feb From 0f88b23cab65e0bf3dc020d27bf9694d0c35b2f5 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 15 May 2020 16:10:45 +1000 Subject: Add Console*LastError macros and delete deprecated ones. --- src/dutil/inc/conutil.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/dutil/inc/conutil.h b/src/dutil/inc/conutil.h index cfb65332..a098cd4c 100644 --- a/src/dutil/inc/conutil.h +++ b/src/dutil/inc/conutil.h @@ -9,12 +9,8 @@ extern "C" { #define ConsoleExitOnFailure(x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } #define ConsoleExitOnLastError(x, c, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } } #define ConsoleExitOnNull(p, x, e, c, f, ...) if (NULL == p) { x = e; ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } - - -// the following macros need to go away -#define ConsoleTrace(l, f, ...) { ConsoleWriteLine(CONSOLE_COLOR_NORMAL, f, __VA_ARGS__); Trace(l, f, __VA_ARGS__); } -#define ConsoleWarning(f, ...) { ConsoleWriteLine(CONSOLE_COLOR_YELLOW, f, __VA_ARGS__); Trace(REPORT_STANDARD, f, __VA_ARGS__); } -#define ConsoleError(x, f, ...) { ConsoleWriteError(x, CONSOLE_COLOR_RED, f, __VA_ARGS__); TraceError(x, f, __VA_ARGS__); } +#define ConsoleExitOnNullWithLastError(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__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } +#define ConsoleExitWithLastError(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__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } // enums -- cgit v1.2.3-55-g6feb From 0d294d6306004a495317d21a220e1a64f392fdc4 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sun, 17 May 2020 17:33:00 -0400 Subject: Port wix3 ARM64 fix. --- src/dutil/procutil.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/dutil/procutil.cpp b/src/dutil/procutil.cpp index 4b34c773..9833d0ec 100644 --- a/src/dutil/procutil.cpp +++ b/src/dutil/procutil.cpp @@ -68,6 +68,24 @@ extern "C" HRESULT DAPI ProcWow64( HRESULT hr = S_OK; BOOL fIsWow64 = FALSE; + typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); + LPFN_ISWOW64PROCESS2 pfnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process2"); + + if (pfnIsWow64Process2) + { + USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN; + if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr)) + { + ExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2."); + } + + if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN) + { + fIsWow64 = TRUE; + } + } + else + { typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process"); @@ -75,7 +93,8 @@ extern "C" HRESULT DAPI ProcWow64( { if (!pfnIsWow64Process(hProcess, &fIsWow64)) { - ExitWithLastError(hr, "Failed to check WOW64 process."); + ExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process."); + } } } -- cgit v1.2.3-55-g6feb From 5a722b5e95b513e169dab7b57126f21aa9b1c374 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sat, 30 May 2020 19:21:28 -0400 Subject: Enable VS2017 ARM/ARM64 builds. --- appveyor.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 49869ace..0d108c00 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -8,8 +8,8 @@ msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141 msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141 -REM msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 -REM msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 +msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 +msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 -- cgit v1.2.3-55-g6feb From b73f7f24be617227f14240fa9ba9e12fa386913d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 16 Jun 2020 17:24:03 +1000 Subject: Fail build for each command. --- appveyor.cmd | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 0d108c00..c54f9161 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -1,22 +1,22 @@ @setlocal @pushd %~dp0 -nuget restore +nuget restore || exit /b -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140 -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140 || exit /b +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 || exit /b -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141 -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141 -msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 -msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 || exit /b -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 -msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v142 -msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 || exit /b -msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj +msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj || exit /b @popd @endlocal \ No newline at end of file -- cgit v1.2.3-55-g6feb From 57536725e6061c7f6cd0f532c07beca7cd1228c9 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 18 Jun 2020 21:39:57 +1000 Subject: Pack v141 ARM and ARM64. --- src/dutil/dutil.nuspec | 2 -- src/dutil/dutil.vcxproj | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec index 2ae420e1..45ac64f0 100644 --- a/src/dutil/dutil.nuspec +++ b/src/dutil/dutil.nuspec @@ -19,10 +19,8 @@ - diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index f44b195c..d6374f19 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -42,8 +42,7 @@ StaticLibrary dutil true - v141_xp - v141 + v142 MultiByte WiX Toolset native library foundation -- cgit v1.2.3-55-g6feb From 7fc25bc32547277c38bbedceb39c454843af8aac Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 22 Jun 2020 19:06:48 +1000 Subject: Replace ExitTrace with new DUTIL_CALLBACK_TRACEERROR callback. This allows capturing internal dutil errors and eventually supports filtering to certain parts of dutil. Add Exit...Source macros to simplify calling both TraceError/TraceErrorDebug and Dutil_TraceErrorSource. Make existing Exit macros call the new Exit...Source macros so the logic is in one place. --- src/dutil/dirutil.cpp | 2 +- src/dutil/dutil.cpp | 41 ++++++++++++++++++++++-- src/dutil/eseutil.cpp | 6 ++-- src/dutil/inc/conutil.h | 16 +++++++--- src/dutil/inc/dutil.h | 83 +++++++++++++++++++++++++++++++++++++----------- src/dutil/inc/gdiputil.h | 3 +- src/dutil/inc/logutil.h | 6 ++-- 7 files changed, 125 insertions(+), 32 deletions(-) diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp index ddd621ac..1f06f551 100644 --- a/src/dutil/dirutil.cpp +++ b/src/dutil/dirutil.cpp @@ -254,7 +254,7 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( if (FAILED(hr)) { // if we failed to delete a subdirectory, keep trying to finish any remaining files - ExitTrace(hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); + ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); hr = S_OK; } } diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp index 3945d8c1..a32de516 100644 --- a/src/dutil/dutil.cpp +++ b/src/dutil/dutil.cpp @@ -13,8 +13,26 @@ static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL; static BOOL Dutil_fNoAsserts = FALSE; static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD; static BOOL Dutil_fTraceFilenames = FALSE; +static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL; +DAPI_(HRESULT) DutilInitialize( + __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback + ) +{ + HRESULT hr = S_OK; + + vpfnTraceErrorCallback = pfnTraceErrorCallback; + + return hr; +} + + +DAPI_(void) DutilUninitialize() +{ + vpfnTraceErrorCallback = NULL; +} + /******************************************************************* Dutil_SetAssertModule @@ -216,7 +234,7 @@ extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel() Dutil_Trace *******************************************************************/ -extern "C" void DAPI Dutil_Trace( +extern "C" void DAPIV Dutil_Trace( __in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, @@ -293,7 +311,7 @@ extern "C" void DAPI Dutil_Trace( Dutil_TraceError *******************************************************************/ -extern "C" void DAPI Dutil_TraceError( +extern "C" void DAPIV Dutil_TraceError( __in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, @@ -380,6 +398,25 @@ extern "C" void DAPI Dutil_TraceError( } +DAPIV_(void) Dutil_TraceErrorSource( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hr, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback) + { + va_list args; + va_start(args, szFormat); + vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args); + va_end(args); + } +} + /******************************************************************* Dutil_RootFailure diff --git a/src/dutil/eseutil.cpp b/src/dutil/eseutil.cpp index 1ff8e82c..599a3943 100644 --- a/src/dutil/eseutil.cpp +++ b/src/dutil/eseutil.cpp @@ -85,13 +85,13 @@ HRESULT HresultFromJetError(JET_ERR jEr) } // 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 - ExitTrace(hr, "Encountered Jet Error: 0x%08x", jEr); + ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Encountered Jet Error: 0x%08x", jEr); return hr; } -#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTrace(x, s, __VA_ARGS__); goto LExit; }} -#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }} +#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__); goto LExit; }} +#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__); goto LExit; }} HRESULT DAPI EseBeginSession( __out JET_INSTANCE *pjiInstance, diff --git a/src/dutil/inc/conutil.h b/src/dutil/inc/conutil.h index a098cd4c..5f611d01 100644 --- a/src/dutil/inc/conutil.h +++ b/src/dutil/inc/conutil.h @@ -6,13 +6,19 @@ extern "C" { #endif -#define ConsoleExitOnFailure(x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } -#define ConsoleExitOnLastError(x, c, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } } -#define ConsoleExitOnNull(p, x, e, c, f, ...) if (NULL == p) { x = e; ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } -#define ConsoleExitOnNullWithLastError(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__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } -#define ConsoleExitWithLastError(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__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } +#define ConsoleExitOnFailureSource(d, x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#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; } } +#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; } +#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; } +#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; } +#define ConsoleExitOnFailure(x, c, f, ...) ConsoleExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) +#define ConsoleExitOnLastError(x, c, f, ...) ConsoleExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) +#define ConsoleExitOnNull(p, x, e, c, f, ...) ConsoleExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, c, f, __VA_ARGS__) +#define ConsoleExitOnNullWithLastError(p, x, c, f, ...) ConsoleExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, c, f, __VA_ARGS__) +#define ConsoleExitWithLastError(x, c, f, ...) ConsoleExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) + // enums typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR; diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h index 3791dd5a..efaeb5a9 100644 --- a/src/dutil/inc/dutil.h +++ b/src/dutil/inc/dutil.h @@ -20,13 +20,44 @@ typedef enum REPORT_LEVEL REPORT_ERROR, // always gets reported, but can never be specified } REPORT_LEVEL; +typedef enum DUTIL_SOURCE +{ + DUTIL_SOURCE_UNKNOWN, + + DUTIL_SOURCE_EXTERNAL = 256, +} DUTIL_SOURCE; + // asserts and traces typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); +typedef void (CALLBACK *DUTIL_CALLBACK_TRACEERROR)( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hr, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + #ifdef __cplusplus extern "C" { #endif +/******************************************************************** + DutilInitialize - initialize dutil. + +*******************************************************************/ +HRESULT DAPI DutilInitialize( + __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback + ); + +/******************************************************************** + DutilUninitialize - uninitialize dutil. + +*******************************************************************/ +void DAPI DutilUninitialize(); + void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule); void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn); void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine); @@ -34,8 +65,9 @@ void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z LPCSTR szM void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames); REPORT_LEVEL DAPI Dutil_TraceGetLevel(); -void __cdecl Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...); -void __cdecl Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); +void DAPIV Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...); +void DAPIV Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); +void 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, ...); void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError); #ifdef __cplusplus @@ -70,28 +102,43 @@ void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT h #endif // DEBUG -// ExitTrace can be overriden -#ifndef ExitTrace -#define ExitTrace TraceError +// DUTIL_SOURCE_DEFAULT can be overriden +#ifndef DUTIL_SOURCE_DEFAULT +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_UNKNOWN #endif // Exit macros -#define ExitFunction() { goto LExit; } -#define ExitFunction1(x) { x; goto LExit; } +#define ExitFunction() { goto LExit; } +#define ExitFunction1(x) { x; goto LExit; } #define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; } -#define ExitOnLastError(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } } -#define ExitOnLastErrorDebugTrace(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; } } -#define ExitWithLastError(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnFailure(x, s, ...) if (FAILED(x)) { ExitTrace(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnRootFailure(x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnFailureDebugTrace(x, s, ...) if (FAILED(x)) { TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnNull(p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnNullWithLastError(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); ExitTrace(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnNullDebugTrace(p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnInvalidHandleWithLastError(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); ExitTrace(x, s, __VA_ARGS__); goto LExit; } -#define ExitOnWin32Error(e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } +#define ExitTraceSource(d, x, s, ...) { TraceError(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_ERROR, d, x, s, __VA_ARGS__); } +#define ExitTraceDebugSource(d, x, s, ...) { TraceErrorDebug(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, d, x, s, __VA_ARGS__); } + +#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; } } +#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; } } +#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; } +#define ExitOnFailureSource(d, x, s, ...) if (FAILED(x)) { ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnRootFailureSource(d, x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnFailureDebugTraceSource(d, x, s, ...) if (FAILED(x)) { ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } +#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; } +#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; } +#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; } +#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; } +#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; } + +#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) +#define ExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) +#define ExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) +#define ExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) +#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__) // release macros #define ReleaseObject(x) if (x) { x->Release(); } diff --git a/src/dutil/inc/gdiputil.h b/src/dutil/inc/gdiputil.h index 3708053c..f2145828 100644 --- a/src/dutil/inc/gdiputil.h +++ b/src/dutil/inc/gdiputil.h @@ -2,7 +2,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -#define ExitOnGdipFailure(g, x, s, ...) { x = GdipHresultFromStatus(g); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } } +#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; } } +#define ExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEFAULT, g, x, s, __VA_ARGS__) #ifdef __cplusplus extern "C" { diff --git a/src/dutil/inc/logutil.h b/src/dutil/inc/logutil.h index ee0cd065..426506ee 100644 --- a/src/dutil/inc/logutil.h +++ b/src/dutil/inc/logutil.h @@ -6,9 +6,11 @@ extern "C" { #endif -#define LogExitOnFailure(x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } +#define LogExitOnFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#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; } -#define LogExitOnRootFailure(x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, f, __VA_ARGS__); goto LExit; } +#define LogExitOnFailure(x, i, f, ...) LogExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) +#define LogExitOnRootFailure(x, i, f, ...) LogExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)( __in_z LPCSTR szString, -- cgit v1.2.3-55-g6feb From 4725cea30832be9113269ef567f196ea3d91cb78 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 22 Jun 2020 19:07:25 +1000 Subject: Add ThmExit macros to allow capturing thmutil specific errors. --- src/dutil/dutil.vcxproj | 1 + src/dutil/dutil.vcxproj.filters | 3 + src/dutil/inc/dutil.h | 8 +- src/dutil/inc/dutilsources.h | 64 +++++ src/dutil/precomp.h | 1 + src/dutil/thmutil.cpp | 554 ++++++++++++++++++++-------------------- 6 files changed, 354 insertions(+), 277 deletions(-) create mode 100644 src/dutil/inc/dutilsources.h diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index d6374f19..af8385d1 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -140,6 +140,7 @@ + diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters index acf7591c..4dd90fdd 100644 --- a/src/dutil/dutil.vcxproj.filters +++ b/src/dutil/dutil.vcxproj.filters @@ -344,6 +344,9 @@ Header Files + + Header Files + diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h index efaeb5a9..6b57b48a 100644 --- a/src/dutil/inc/dutil.h +++ b/src/dutil/inc/dutil.h @@ -1,6 +1,7 @@ #pragma once // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +#include "dutilsources.h" #define DAPI __stdcall #define DAPIV __cdecl // used only for functions taking variable length arguments @@ -20,13 +21,6 @@ typedef enum REPORT_LEVEL REPORT_ERROR, // always gets reported, but can never be specified } REPORT_LEVEL; -typedef enum DUTIL_SOURCE -{ - DUTIL_SOURCE_UNKNOWN, - - DUTIL_SOURCE_EXTERNAL = 256, -} DUTIL_SOURCE; - // asserts and traces typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h new file mode 100644 index 00000000..c88ada37 --- /dev/null +++ b/src/dutil/inc/dutilsources.h @@ -0,0 +1,64 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +typedef enum DUTIL_SOURCE +{ + DUTIL_SOURCE_UNKNOWN, + DUTIL_SOURCE_ACLUTIL, + DUTIL_SOURCE_APPUTIL, + DUTIL_SOURCE_APUPUTIL, + DUTIL_SOURCE_ATOMUTIL, + DUTIL_SOURCE_BUFFUTIL, + DUTIL_SOURCE_BUTIL, + DUTIL_SOURCE_CABCUTIL, + DUTIL_SOURCE_CABUTIL, + DUTIL_SOURCE_CERTUTIL, + DUTIL_SOURCE_CONUTIL, + DUTIL_SOURCE_CRYPUTIL, + DUTIL_SOURCE_DEPUTIL, + DUTIL_SOURCE_DICTUTIL, + DUTIL_SOURCE_DIRUTIL, + DUTIL_SOURCE_DLUTIL, + DUTIL_SOURCE_DUTIL, + DUTIL_SOURCE_ESEUTIL, + DUTIL_SOURCE_FILEUTIL, + DUTIL_SOURCE_GDIPUTIL, + DUTIL_SOURCE_GUIDUTIL, + DUTIL_SOURCE_IIS7UTIL, + DUTIL_SOURCE_INETUTIL, + DUTIL_SOURCE_INIUTIL, + DUTIL_SOURCE_JSONUTIL, + DUTIL_SOURCE_LOCUTIL, + DUTIL_SOURCE_LOGUTIL, + DUTIL_SOURCE_MEMUTIL, + DUTIL_SOURCE_METAUTIL, + DUTIL_SOURCE_MONUTIL, + DUTIL_SOURCE_OSUTIL, + DUTIL_SOURCE_PATHUTIL, + DUTIL_SOURCE_PERFUTIL, + DUTIL_SOURCE_POLCUTIL, + DUTIL_SOURCE_PROCUTIL, + DUTIL_SOURCE_REGUTIL, + DUTIL_SOURCE_RESRUTIL, + DUTIL_SOURCE_RESWUTIL, + DUTIL_SOURCE_REXUTIL, + DUTIL_SOURCE_RMUTIL, + DUTIL_SOURCE_RSSUTIL, + DUTIL_SOURCE_SCEUTIL, + DUTIL_SOURCE_SCZUTIL, + DUTIL_SOURCE_SHELUTIL, + DUTIL_SOURCE_SQLUTIL, + DUTIL_SOURCE_SRPUTIL, + DUTIL_SOURCE_STRUTIL, + DUTIL_SOURCE_SVCUTIL, + DUTIL_SOURCE_THMUTIL, + DUTIL_SOURCE_TIMEUTIL, + DUTIL_SOURCE_UNCUTIL, + DUTIL_SOURCE_URIUTIL, + DUTIL_SOURCE_USERUTIL, + DUTIL_SOURCE_WIUTIL, + DUTIL_SOURCE_WUAUTIL, + DUTIL_SOURCE_XMLUTIL, + + DUTIL_SOURCE_EXTERNAL = 256, +} DUTIL_SOURCE; diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h index 374d0fc1..eebdd160 100644 --- a/src/dutil/precomp.h +++ b/src/dutil/precomp.h @@ -40,6 +40,7 @@ #include #include +#include "dutilsources.h" #include "dutil.h" #include "aclutil.h" #include "atomutil.h" diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index cae92d92..1939c64d 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -3,6 +3,20 @@ #include "precomp.h" +// Exit macros +#define ThmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) +#define ThmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) +#define ThmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) +#define ThmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) +#define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__) +#define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__) + #ifndef BS_COMMANDLINK #define BS_COMMANDLINK 0x0000000EL #endif @@ -362,16 +376,16 @@ DAPI_(HRESULT) ThemeInitialize( INITCOMMONCONTROLSEX icex = { }; hr = XmlInitialize(); - ExitOnFailure(hr, "Failed to initialize XML."); + ThmExitOnFailure(hr, "Failed to initialize XML."); hr = RegisterWindowClasses(hModule); - ExitOnFailure(hr, "Failed to register theme window classes."); + ThmExitOnFailure(hr, "Failed to register theme window classes."); // Initialize GDI+ and common controls. vgsi.SuppressBackgroundThread = TRUE; hr = GdipInitialize(&vgsi, &vgdiToken, &vgso); - ExitOnFailure(hr, "Failed to initialize GDI+."); + ThmExitOnFailure(hr, "Failed to initialize GDI+."); icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS; @@ -424,13 +438,13 @@ DAPI_(HRESULT) ThemeLoadFromFile( LPWSTR sczRelativePath = NULL; hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd); - ExitOnFailure(hr, "Failed to load theme resource as XML document."); + ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); hr = PathGetDirectory(wzThemeFile, &sczRelativePath); - ExitOnFailure(hr, "Failed to get relative path from theme file."); + ThmExitOnFailure(hr, "Failed to get relative path from theme file."); hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme); - ExitOnFailure(hr, "Failed to parse theme."); + ThmExitOnFailure(hr, "Failed to parse theme."); LExit: ReleaseStr(sczRelativePath); @@ -453,16 +467,16 @@ DAPI_(HRESULT) ThemeLoadFromResource( IXMLDOMDocument* pixd = NULL; hr = ResReadData(hModule, szResource, &pvResource, &cbResource); - ExitOnFailure(hr, "Failed to read theme from resource."); + ThmExitOnFailure(hr, "Failed to read theme from resource."); hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); - ExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + ThmExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); hr = XmlLoadDocument(sczXml, &pixd); - ExitOnFailure(hr, "Failed to load theme resource as XML document."); + ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); hr = ParseTheme(hModule, NULL, pixd, ppTheme); - ExitOnFailure(hr, "Failed to parse theme."); + ThmExitOnFailure(hr, "Failed to parse theme."); LExit: ReleaseObject(pixd); @@ -524,7 +538,7 @@ DAPI_(HRESULT) ThemeRegisterVariableCallbacks( ) { HRESULT hr = S_OK; - ExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); pTheme->pfnEvaluateCondition = pfnEvaluateCondition; pTheme->pfnFormatString = pfnFormatString; @@ -569,7 +583,7 @@ DAPI_(HRESULT) ThemeLocalize( LPWSTR sczCaption = NULL; hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption); - ExitOnFailure(hr, "Failed to localize theme caption."); + ThmExitOnFailure(hr, "Failed to localize theme caption."); if (pTheme->pfnFormatString) { @@ -599,12 +613,12 @@ DAPI_(HRESULT) ThemeLoadStrings( ) { HRESULT hr = S_OK; - ExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); if (UINT_MAX != pTheme->uStringId) { hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption); - ExitOnFailure(hr, "Failed to load theme caption."); + ThmExitOnFailure(hr, "Failed to load theme caption."); } hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule); @@ -627,12 +641,12 @@ DAPI_(HRESULT) ThemeLoadRichEditFromFile( HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); hr = PathRelativeToModule(&sczFile, wzFileName, hModule); - ExitOnFailure(hr, "Failed to read resource data."); + ThmExitOnFailure(hr, "Failed to read resource data."); hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) { - ExitWithLastError(hr, "Failed to open RTF file."); + ThmExitWithLastError(hr, "Failed to open RTF file."); } else { @@ -649,7 +663,7 @@ DAPI_(HRESULT) ThemeLoadRichEditFromFile( ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); hr = es.dwError; - ExitOnFailure(hr, "Failed to update RTF stream."); + ThmExitOnFailure(hr, "Failed to update RTF stream."); } LExit: @@ -682,14 +696,14 @@ DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( EDITSTREAM es = { }; hr = ResReadData(hModule, szResourceName, reinterpret_cast(&buffer.rgbData), &buffer.cbData); - ExitOnFailure(hr, "Failed to read resource data."); + ThmExitOnFailure(hr, "Failed to read resource data."); es.pfnCallback = RichEditStreamFromMemoryCallback; es.dwCookie = reinterpret_cast(&buffer); ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); hr = es.dwError; - ExitOnFailure(hr, "Failed to update RTF stream."); + ThmExitOnFailure(hr, "Failed to update RTF stream."); LExit: return hr; @@ -985,7 +999,7 @@ DAPI_(HRESULT) ThemeShowPageEx( else { hr = MemEnsureArraySize(reinterpret_cast(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices); - ExitOnNull(pPage->rgSavedVariables, hr, E_OUTOFMEMORY, "Failed to allocate memory for saved variables."); + ThmExitOnNull(pPage->rgSavedVariables, hr, E_OUTOFMEMORY, "Failed to allocate memory for saved variables."); SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); pPage->cSavedVariables = pPage->cControlIndices; @@ -998,7 +1012,7 @@ DAPI_(HRESULT) ThemeShowPageEx( } hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage); - ExitOnFailure(hr, "Failed to show page controls."); + ThmExitOnFailure(hr, "Failed to show page controls."); LExit: return hr; @@ -1184,27 +1198,27 @@ DAPI_(HRESULT) ThemeDrawControl( { case THEME_CONTROL_TYPE_BUTTON: hr = DrawButton(pTheme, pdis, pControl); - ExitOnFailure(hr, "Failed to draw button."); + ThmExitOnFailure(hr, "Failed to draw button."); break; case THEME_CONTROL_TYPE_HYPERLINK: hr = DrawHyperlink(pTheme, pdis, pControl); - ExitOnFailure(hr, "Failed to draw hyperlink."); + ThmExitOnFailure(hr, "Failed to draw hyperlink."); break; case THEME_CONTROL_TYPE_IMAGE: hr = DrawImage(pTheme, pdis, pControl); - ExitOnFailure(hr, "Failed to draw image."); + ThmExitOnFailure(hr, "Failed to draw image."); break; case THEME_CONTROL_TYPE_PROGRESSBAR: hr = DrawProgressBar(pTheme, pdis, pControl); - ExitOnFailure(hr, "Failed to draw progress bar."); + ThmExitOnFailure(hr, "Failed to draw progress bar."); break; default: hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Did not specify an owner draw control to draw."); + ThmExitOnRootFailure(hr, "Did not specify an owner draw control to draw."); } LExit: @@ -1321,7 +1335,7 @@ DAPI_(HRESULT) ThemeSetProgressControl( { if (!::InvalidateRect(hWnd, NULL, FALSE)) { - ExitWithLastError(hr, "Failed to invalidate progress bar window."); + ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); } } else @@ -1367,7 +1381,7 @@ DAPI_(HRESULT) ThemeSetProgressControlColor( if (!::InvalidateRect(hWnd, NULL, FALSE)) { - ExitWithLastError(hr, "Failed to invalidate progress bar window."); + ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); } hr = S_OK; @@ -1409,7 +1423,7 @@ DAPI_(HRESULT) ThemeSetTextControlEx( if (!::SetWindowTextW(hWnd, wzText)) { - ExitWithLastError(hr, "Failed to set control text."); + ThmExitWithLastError(hr, "Failed to set control text."); } if (fUpdate) @@ -1436,14 +1450,14 @@ DAPI_(HRESULT) ThemeGetTextControl( // Ensure the string has room for at least one character. hr = StrMaxLength(*psczText, reinterpret_cast(&cchText)); - ExitOnFailure(hr, "Failed to get text buffer length."); + ThmExitOnFailure(hr, "Failed to get text buffer length."); if (!cchText) { cchText = GROW_WINDOW_TEXT; hr = StrAlloc(psczText, cchText); - ExitOnFailure(hr, "Failed to grow text buffer."); + ThmExitOnFailure(hr, "Failed to grow text buffer."); } // Read (and keep growing buffer) until we finally read less than there @@ -1460,7 +1474,7 @@ DAPI_(HRESULT) ThemeGetTextControl( cchText = cchTextRead + GROW_WINDOW_TEXT; hr = StrAlloc(psczText, cchText); - ExitOnFailure(hr, "Failed to grow text buffer again."); + ThmExitOnFailure(hr, "Failed to grow text buffer again."); } } @@ -1477,7 +1491,7 @@ DAPI_(HRESULT) ThemeUpdateCaption( HRESULT hr = S_OK; hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0); - ExitOnFailure(hr, "Failed to update theme caption."); + ThmExitOnFailure(hr, "Failed to update theme caption."); LExit: return hr; @@ -1532,7 +1546,7 @@ static HRESULT RegisterWindowClasses( // Base the theme hyperlink class on a button but give it the "hand" icon. if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink)) { - ExitWithLastError(hr, "Failed to get button window class."); + ThmExitWithLastError(hr, "Failed to get button window class."); } wcHyperlink.lpszClassName = THEME_WC_HYPERLINK; @@ -1543,7 +1557,7 @@ static HRESULT RegisterWindowClasses( if (!::RegisterClassW(&wcHyperlink)) { - ExitWithLastError(hr, "Failed to get button window class."); + ThmExitWithLastError(hr, "Failed to get button window class."); } vhHyperlinkRegisteredModule = hModule; @@ -1554,7 +1568,7 @@ static HRESULT RegisterWindowClasses( wcPanel.lpszClassName = THEME_WC_PANEL; if (!::RegisterClassW(&wcPanel)) { - ExitWithLastError(hr, "Failed to register window."); + ThmExitWithLastError(hr, "Failed to register window."); } vhPanelRegisteredModule = hModule; @@ -1577,24 +1591,24 @@ static HRESULT ParseTheme( IXMLDOMElement *pThemeElement = NULL; hr = pixd->get_documentElement(&pThemeElement); - ExitOnFailure(hr, "Failed to get theme element."); + ThmExitOnFailure(hr, "Failed to get theme element."); pTheme = static_cast(MemAlloc(sizeof(THEME), TRUE)); - ExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); + ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); pTheme->wId = ++wThemeId; // Parse the optional background resource image. hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); - ExitOnFailure(hr, "Failed while parsing theme image."); + ThmExitOnFailure(hr, "Failed while parsing theme image."); // Parse the fonts. hr = ParseFonts(pThemeElement, pTheme); - ExitOnFailure(hr, "Failed to parse theme fonts."); + ThmExitOnFailure(hr, "Failed to parse theme fonts."); // Parse the window element. hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme); - ExitOnFailure(hr, "Failed to parse theme window element."); + ThmExitOnFailure(hr, "Failed to parse theme window element."); *ppTheme = pTheme; pTheme = NULL; @@ -1624,7 +1638,7 @@ static HRESULT ParseImage( Gdiplus::Bitmap* pBitmap = NULL; hr = XmlGetAttribute(pElement, L"ImageResource", &bstr); - ExitOnFailure(hr, "Failed to get image resource attribute."); + ThmExitOnFailure(hr, "Failed to get image resource attribute."); if (S_OK == hr) { @@ -1640,19 +1654,19 @@ static HRESULT ParseImage( if (!pBitmap) { hr = XmlGetAttribute(pElement, L"ImageFile", &bstr); - ExitOnFailure(hr, "Failed to get image file attribute."); + ThmExitOnFailure(hr, "Failed to get image file attribute."); if (S_OK == hr) { if (wzRelativePath) { hr = PathConcat(wzRelativePath, bstr, &sczImageFile); - ExitOnFailure(hr, "Failed to combine image file path."); + ThmExitOnFailure(hr, "Failed to combine image file path."); } else { hr = PathRelativeToModule(&sczImageFile, bstr, hModule); - ExitOnFailure(hr, "Failed to get image filename."); + ThmExitOnFailure(hr, "Failed to get image filename."); } hr = GdipBitmapFromFile(sczImageFile, &pBitmap); @@ -1665,7 +1679,7 @@ static HRESULT ParseImage( { Gdiplus::Color black; Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage); - ExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP."); + ThmExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP."); } hr = S_OK; @@ -1696,37 +1710,37 @@ static HRESULT ParseIcon( int iResourceId = 0; hr = XmlGetAttribute(pElement, L"IconResource", &bstr); - ExitOnFailure(hr, "Failed to get icon resource attribute."); + ThmExitOnFailure(hr, "Failed to get icon resource attribute."); if (S_OK == hr) { iResourceId = wcstol(bstr, NULL, 10); *phIcon = reinterpret_cast(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); - ExitOnNullWithLastError(*phIcon, hr, "Failed to load icon."); + ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon."); } else { ReleaseNullBSTR(bstr); hr = XmlGetAttribute(pElement, L"IconFile", &bstr); - ExitOnFailure(hr, "Failed to get icon file attribute."); + ThmExitOnFailure(hr, "Failed to get icon file attribute."); if (S_OK == hr) { if (wzRelativePath) { hr = PathConcat(wzRelativePath, bstr, &sczImageFile); - ExitOnFailure(hr, "Failed to combine image file path."); + ThmExitOnFailure(hr, "Failed to combine image file path."); } else { hr = PathRelativeToModule(&sczImageFile, bstr, hModule); - ExitOnFailure(hr, "Failed to get image filename."); + ThmExitOnFailure(hr, "Failed to get image filename."); } *phIcon = reinterpret_cast(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)); - ExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile); + ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile); } } @@ -1755,84 +1769,84 @@ static HRESULT ParseWindow( { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find window element."); + ThmExitOnFailure(hr, "Failed to find window element."); hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize); if (E_NOTFOUND == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get window AutoResize attribute."); + ThmExitOnFailure(hr, "Failed to get window AutoResize attribute."); hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pTheme->nWidth)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find window Width attribute."); + ThmExitOnRootFailure(hr, "Failed to find window Width attribute."); } - ExitOnFailure(hr, "Failed to get window Width attribute."); + ThmExitOnFailure(hr, "Failed to get window Width attribute."); hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pTheme->nHeight)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find window Height attribute."); + ThmExitOnRootFailure(hr, "Failed to find window Height attribute."); } - ExitOnFailure(hr, "Failed to get window Height attribute."); + ThmExitOnFailure(hr, "Failed to get window Height attribute."); hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", reinterpret_cast(&pTheme->nMinimumWidth)); if (S_FALSE == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); + ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", reinterpret_cast(&pTheme->nMinimumHeight)); if (S_FALSE == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); + ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find window FontId attribute."); + ThmExitOnRootFailure(hr, "Failed to find window FontId attribute."); } - ExitOnFailure(hr, "Failed to get window FontId attribute."); + ThmExitOnFailure(hr, "Failed to get window FontId attribute."); // Get the optional window icon from a resource. hr = XmlGetAttribute(pixn, L"IconResource", &bstr); - ExitOnFailure(hr, "Failed to get window IconResource attribute."); + ThmExitOnFailure(hr, "Failed to get window IconResource attribute."); if (S_OK == hr) { pTheme->hIcon = ::LoadIconW(hModule, bstr); - ExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource."); + ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource."); ReleaseNullBSTR(bstr); } // Get the optional window icon from a file. hr = XmlGetAttribute(pixn, L"IconFile", &bstr); - ExitOnFailure(hr, "Failed to get window IconFile attribute."); + ThmExitOnFailure(hr, "Failed to get window IconFile attribute."); if (S_OK == hr) { if (wzRelativePath) { hr = PathConcat(wzRelativePath, bstr, &sczIconFile); - ExitOnFailure(hr, "Failed to combine icon file path."); + ThmExitOnFailure(hr, "Failed to combine icon file path."); } else { hr = PathRelativeToModule(&sczIconFile, bstr, hModule); - ExitOnFailure(hr, "Failed to get icon filename."); + ThmExitOnFailure(hr, "Failed to get icon filename."); } pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); - ExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr); + ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr); ReleaseNullBSTR(bstr); } @@ -1842,18 +1856,18 @@ static HRESULT ParseWindow( { pTheme->nSourceX = -1; } - ExitOnFailure(hr, "Failed to get window SourceX attribute."); + ThmExitOnFailure(hr, "Failed to get window SourceX attribute."); hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pTheme->nSourceY)); if (S_FALSE == hr) { pTheme->nSourceY = -1; } - ExitOnFailure(hr, "Failed to get window SourceY attribute."); + ThmExitOnFailure(hr, "Failed to get window SourceY attribute."); // Parse the optional window style. hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle); - ExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute."); + ThmExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute."); if (S_FALSE == hr) { @@ -1862,36 +1876,36 @@ static HRESULT ParseWindow( } hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pTheme->uStringId)); - ExitOnFailure(hr, "Failed to get window StringId attribute."); + ThmExitOnFailure(hr, "Failed to get window StringId attribute."); if (S_FALSE == hr) { pTheme->uStringId = UINT_MAX; hr = XmlGetAttribute(pixn, L"Caption", &bstr); - ExitOnFailure(hr, "Failed to get window Caption attribute."); + ThmExitOnFailure(hr, "Failed to get window Caption attribute."); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute."); + ThmExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute."); } hr = StrAllocString(&pTheme->sczCaption, bstr, 0); - ExitOnFailure(hr, "Failed to copy window Caption attribute."); + ThmExitOnFailure(hr, "Failed to copy window Caption attribute."); } // Parse any image lists. hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme); - ExitOnFailure(hr, "Failed to parse image lists."); + ThmExitOnFailure(hr, "Failed to parse image lists."); // Parse the pages. hr = ParsePages(hModule, wzRelativePath, pixn, pTheme); - ExitOnFailure(hr, "Failed to parse theme pages."); + ThmExitOnFailure(hr, "Failed to parse theme pages."); // Parse the non-paged controls. hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL); - ExitOnFailure(hr, "Failed to parse theme controls."); + ThmExitOnFailure(hr, "Failed to parse theme controls."); LExit: ReleaseStr(sczIconFile); @@ -1919,10 +1933,10 @@ static HRESULT ParseFonts( DWORD dwSystemBackgroundColor = FALSE; hr = XmlSelectNodes(pElement, L"Font", &pixnl); - ExitOnFailure(hr, "Failed to find font elements."); + ThmExitOnFailure(hr, "Failed to find font elements."); hr = pixnl->get_length(reinterpret_cast(&pTheme->cFonts)); - ExitOnFailure(hr, "Failed to count the number of theme fonts."); + ThmExitOnFailure(hr, "Failed to count the number of theme fonts."); if (!pTheme->cFonts) { @@ -1930,7 +1944,7 @@ static HRESULT ParseFonts( } pTheme->rgFonts = static_cast(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE)); - ExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); + ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); lf.lfQuality = CLEARTYPE_QUALITY; @@ -1941,12 +1955,12 @@ static HRESULT ParseFonts( { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find font id."); + ThmExitOnFailure(hr, "Failed to find font id."); if (pTheme->cFonts <= dwId) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Invalid theme font id."); + ThmExitOnRootFailure(hr, "Invalid theme font id."); } hr = XmlGetText(pixn, &bstrName); @@ -1954,17 +1968,17 @@ static HRESULT ParseFonts( { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to get font name."); + ThmExitOnFailure(hr, "Failed to get font name."); hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), bstrName); - ExitOnFailure(hr, "Failed to copy font name."); + ThmExitOnFailure(hr, "Failed to copy font name."); hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&lf.lfHeight)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find font height attribute."); + ThmExitOnFailure(hr, "Failed to find font height attribute."); hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&lf.lfWeight)); if (S_FALSE == hr) @@ -1972,7 +1986,7 @@ static HRESULT ParseFonts( lf.lfWeight = FW_DONTCARE; hr = S_OK; } - ExitOnFailure(hr, "Failed to find font weight attribute."); + ThmExitOnFailure(hr, "Failed to find font weight attribute."); hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&lf.lfUnderline)); if (E_NOTFOUND == hr) @@ -1980,42 +1994,42 @@ static HRESULT ParseFonts( lf.lfUnderline = FALSE; hr = S_OK; } - ExitOnFailure(hr, "Failed to find font underline attribute."); + ThmExitOnFailure(hr, "Failed to find font underline attribute."); hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor); - ExitOnFailure(hr, "Failed to find font foreground color."); + ThmExitOnFailure(hr, "Failed to find font foreground color."); hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor); - ExitOnFailure(hr, "Failed to find font background color."); + ThmExitOnFailure(hr, "Failed to find font background color."); THEME_FONT* pFont = pTheme->rgFonts + dwId; if (pFont->hFont) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Theme font id duplicated."); + ThmExitOnRootFailure(hr, "Theme font id duplicated."); } pFont->hFont = ::CreateFontIndirectW(&lf); - ExitOnNullWithLastError(pFont->hFont, hr, "Failed to create font %u.", dwId); + ThmExitOnNullWithLastError(pFont->hFont, hr, "Failed to create font %u.", dwId); pFont->crForeground = crForeground; if (THEME_INVISIBLE_COLORREF != pFont->crForeground) { pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground); - ExitOnNullWithLastError(pFont->hForeground, hr, "Failed to create text foreground brush."); + ThmExitOnNullWithLastError(pFont->hForeground, hr, "Failed to create text foreground brush."); } pFont->crBackground = crBackground; if (THEME_INVISIBLE_COLORREF != pFont->crBackground) { pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground); - ExitOnNullWithLastError(pFont->hBackground, hr, "Failed to create text background brush."); + ThmExitOnNullWithLastError(pFont->hBackground, hr, "Failed to create text background brush."); } ReleaseNullBSTR(bstrName); ReleaseNullObject(pixn); } - ExitOnFailure(hr, "Failed to enumerate all fonts."); + ThmExitOnFailure(hr, "Failed to enumerate all fonts."); if (S_FALSE == hr) { @@ -2049,7 +2063,7 @@ static HRESULT GetFontColor( *pColorRef = THEME_INVISIBLE_COLORREF; ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName); + ThmExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName); if (pdwSystemColor) { @@ -2117,10 +2131,10 @@ static HRESULT ParsePages( DWORD iPage = 0; hr = XmlSelectNodes(pElement, L"Page", &pixnl); - ExitOnFailure(hr, "Failed to find page elements."); + ThmExitOnFailure(hr, "Failed to find page elements."); hr = pixnl->get_length(reinterpret_cast(&pTheme->cPages)); - ExitOnFailure(hr, "Failed to count the number of theme pages."); + ThmExitOnFailure(hr, "Failed to count the number of theme pages."); if (!pTheme->cPages) { @@ -2128,7 +2142,7 @@ static HRESULT ParsePages( } pTheme->rgPages = static_cast(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE)); - ExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages."); + ThmExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages."); while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) { @@ -2141,17 +2155,17 @@ static HRESULT ParsePages( { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying page Name."); + ThmExitOnFailure(hr, "Failed when querying page Name."); hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage); - ExitOnFailure(hr, "Failed to parse page controls."); + ThmExitOnFailure(hr, "Failed to parse page controls."); ++iPage; ReleaseNullBSTR(bstrType); ReleaseNullObject(pixn); } - ExitOnFailure(hr, "Failed to enumerate all pages."); + ThmExitOnFailure(hr, "Failed to enumerate all pages."); if (S_FALSE == hr) { @@ -2188,10 +2202,10 @@ static HRESULT ParseImageLists( int iRetVal = 0; hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists); - ExitOnFailure(hr, "Failed to find ImageList elements."); + ThmExitOnFailure(hr, "Failed to find ImageList elements."); hr = pixnlImageLists->get_length(reinterpret_cast(&pTheme->cImageLists)); - ExitOnFailure(hr, "Failed to count the number of image lists."); + ThmExitOnFailure(hr, "Failed to count the number of image lists."); if (!pTheme->cImageLists) { @@ -2199,7 +2213,7 @@ static HRESULT ParseImageLists( } pTheme->rgImageLists = static_cast(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE)); - ExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists."); + ThmExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists."); while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL))) { @@ -2208,16 +2222,16 @@ static HRESULT ParseImageLists( { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find ImageList/@Name attribute."); + ThmExitOnFailure(hr, "Failed to find ImageList/@Name attribute."); hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0); - ExitOnFailure(hr, "Failed to make copy of ImageList name."); + ThmExitOnFailure(hr, "Failed to make copy of ImageList name."); hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages); - ExitOnFailure(hr, "Failed to select child Image nodes."); + ThmExitOnFailure(hr, "Failed to select child Image nodes."); hr = pixnlImages->get_length(reinterpret_cast(&dwImageCount)); - ExitOnFailure(hr, "Failed to count the number of images in list."); + ThmExitOnFailure(hr, "Failed to count the number of images in list."); if (0 < dwImageCount) { @@ -2230,20 +2244,20 @@ static HRESULT ParseImageLists( hBitmap = NULL; } hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap); - ExitOnFailure(hr, "Failed to parse image: %u", i); + ThmExitOnFailure(hr, "Failed to parse image: %u", i); if (0 == i) { ::GetObjectW(hBitmap, sizeof(BITMAP), &bm); pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0); - ExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list."); + ThmExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list."); } iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL); if (-1 == iRetVal) { - ExitWithLastError(hr, "Failed to add image %u to image list.", i); + ThmExitWithLastError(hr, "Failed to add image %u to image list.", i); } ++i; @@ -2326,13 +2340,13 @@ static HRESULT ParseControls( GetControls(pTheme, pParentControl, &pcControls, &prgControls); hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage); - ExitOnFailure(hr, "Failed to parse radio buttons."); + ThmExitOnFailure(hr, "Failed to parse radio buttons."); hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl); - ExitOnFailure(hr, "Failed to find control elements."); + ThmExitOnFailure(hr, "Failed to find control elements."); hr = pixnl->get_length(reinterpret_cast(&cNewControls)); - ExitOnFailure(hr, "Failed to count the number of theme controls."); + ThmExitOnFailure(hr, "Failed to count the number of theme controls."); if (!cNewControls) { @@ -2340,7 +2354,7 @@ static HRESULT ParseControls( } hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls); - ExitOnFailure(hr, "Failed to reallocate theme controls."); + ThmExitOnFailure(hr, "Failed to reallocate theme controls."); cNewControls += *pcControls; @@ -2360,7 +2374,7 @@ static HRESULT ParseControls( if (!bstrType) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Null element encountered!"); + ThmExitOnFailure(hr, "Null element encountered!"); } if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1)) @@ -2441,7 +2455,7 @@ static HRESULT ParseControls( BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type; hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage); - ExitOnFailure(hr, "Failed to parse control."); + ThmExitOnFailure(hr, "Failed to parse control."); if (fBillboardSizing) { @@ -2463,7 +2477,7 @@ static HRESULT ParseControls( ReleaseNullBSTR(bstrType); ReleaseNullObject(pixn); } - ExitOnFailure(hr, "Failed to enumerate all controls."); + ThmExitOnFailure(hr, "Failed to enumerate all controls."); if (S_FALSE == hr) { @@ -2503,21 +2517,21 @@ static HRESULT ParseControl( { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying control Name attribute."); + ThmExitOnFailure(hr, "Failed when querying control Name attribute."); hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition); if (E_NOTFOUND == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying control EnableCondition attribute."); + ThmExitOnFailure(hr, "Failed when querying control EnableCondition attribute."); hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition); if (E_NOTFOUND == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying control VisibleCondition attribute."); + ThmExitOnFailure(hr, "Failed when querying control VisibleCondition attribute."); if (!fSkipDimensions) { @@ -2526,58 +2540,58 @@ static HRESULT ParseControl( { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find control X attribute."); + ThmExitOnFailure(hr, "Failed to find control X attribute."); hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pControl->nY)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find control Y attribute."); + ThmExitOnFailure(hr, "Failed to find control Y attribute."); hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pControl->nHeight)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find control Height attribute."); + ThmExitOnFailure(hr, "Failed to find control Height attribute."); hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pControl->nWidth)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to find control Width attribute."); + ThmExitOnFailure(hr, "Failed to find control Width attribute."); } // Parse the optional background resource image. hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage); - ExitOnFailure(hr, "Failed while parsing control image."); + ThmExitOnFailure(hr, "Failed while parsing control image."); hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pControl->nSourceX)); if (S_FALSE == hr) { pControl->nSourceX = -1; } - ExitOnFailure(hr, "Failed when querying control SourceX attribute."); + ThmExitOnFailure(hr, "Failed when querying control SourceX attribute."); hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pControl->nSourceY)); if (S_FALSE == hr) { pControl->nSourceY = -1; } - ExitOnFailure(hr, "Failed when querying control SourceY attribute."); + ThmExitOnFailure(hr, "Failed when querying control SourceY attribute."); hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId); if (S_FALSE == hr) { pControl->dwFontId = THEME_INVALID_ID; } - ExitOnFailure(hr, "Failed when querying control FontId attribute."); + ThmExitOnFailure(hr, "Failed when querying control FontId attribute."); // Parse the optional window style. hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle); - ExitOnFailure(hr, "Failed when querying control HexStyle attribute."); + ThmExitOnFailure(hr, "Failed when querying control HexStyle attribute."); // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above. hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue); @@ -2587,7 +2601,7 @@ static HRESULT ParseControl( } else { - ExitOnFailure(hr, "Failed when querying control TabStop attribute."); + ThmExitOnFailure(hr, "Failed when querying control TabStop attribute."); if (fValue) { @@ -2602,7 +2616,7 @@ static HRESULT ParseControl( } else { - ExitOnFailure(hr, "Failed when querying control Visible attribute."); + ThmExitOnFailure(hr, "Failed when querying control Visible attribute."); if (fValue) { @@ -2617,7 +2631,7 @@ static HRESULT ParseControl( } else { - ExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute."); + ThmExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute."); if (fValue) { @@ -2632,22 +2646,22 @@ static HRESULT ParseControl( } else { - ExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute."); + ThmExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute."); } hr = ParseActions(pixn, pControl); - ExitOnFailure(hr, "Failed to parse action nodes of the control."); + ThmExitOnFailure(hr, "Failed to parse action nodes of the control."); hr = ParseText(pixn, pControl, &fAnyTextChildren); - ExitOnFailure(hr, "Failed to parse text nodes of the control."); + ThmExitOnFailure(hr, "Failed to parse text nodes of the control."); hr = ParseTooltips(pixn, pControl, &fAnyTextChildren); - ExitOnFailure(hr, "Failed to parse control Tooltip."); + ThmExitOnFailure(hr, "Failed to parse control Tooltip."); if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) { hr = ParseNotes(pixn, pControl, &fAnyNoteChildren); - ExitOnFailure(hr, "Failed to parse note text nodes of the control."); + ThmExitOnFailure(hr, "Failed to parse note text nodes of the control."); } if (fAnyTextChildren || fAnyNoteChildren) @@ -2657,7 +2671,7 @@ static HRESULT ParseControl( else { hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pControl->uStringId)); - ExitOnFailure(hr, "Failed when querying control StringId attribute."); + ThmExitOnFailure(hr, "Failed when querying control StringId attribute."); if (S_FALSE == hr) { @@ -2671,12 +2685,12 @@ static HRESULT ParseControl( else { hr = XmlGetText(pixn, &bstrText); - ExitOnFailure(hr, "Failed to get control inner text."); + ThmExitOnFailure(hr, "Failed to get control inner text."); if (S_OK == hr) { hr = StrAllocString(&pControl->sczText, bstrText, 0); - ExitOnFailure(hr, "Failed to copy control text."); + ThmExitOnFailure(hr, "Failed to copy control text."); ReleaseNullBSTR(bstrText); } @@ -2695,7 +2709,7 @@ static HRESULT ParseControl( { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute."); + ThmExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute."); pControl->wBillboardInterval = 5000; hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue); @@ -2703,15 +2717,15 @@ static HRESULT ParseControl( { pControl->wBillboardInterval = static_cast(dwValue & 0xFFFF); } - ExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute."); + ThmExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute."); hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); - ExitOnFailure(hr, "Failed to parse billboard children."); + ThmExitOnFailure(hr, "Failed to parse billboard children."); } else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) { hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon); - ExitOnFailure(hr, "Failed while parsing control icon."); + ThmExitOnFailure(hr, "Failed while parsing control icon."); } else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) { @@ -2722,7 +2736,7 @@ static HRESULT ParseControl( } else { - ExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute."); + ThmExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute."); if (fValue) { @@ -2737,14 +2751,14 @@ static HRESULT ParseControl( { pControl->dwFontHoverId = THEME_INVALID_ID; } - ExitOnFailure(hr, "Failed when querying control HoverFontId attribute."); + ThmExitOnFailure(hr, "Failed when querying control HoverFontId attribute."); hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId); if (S_FALSE == hr) { pControl->dwFontSelectedId = THEME_INVALID_ID; } - ExitOnFailure(hr, "Failed when querying control SelectedFontId attribute."); + ThmExitOnFailure(hr, "Failed when querying control SelectedFontId attribute."); } else if (THEME_CONTROL_TYPE_LABEL == pControl->type) { @@ -2757,7 +2771,7 @@ static HRESULT ParseControl( { pControl->dwStyle |= SS_CENTER; } - ExitOnFailure(hr, "Failed when querying Label/@Center attribute."); + ThmExitOnFailure(hr, "Failed when querying Label/@Center attribute."); hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue); if (E_NOTFOUND == hr) @@ -2768,57 +2782,57 @@ static HRESULT ParseControl( { pControl->dwStyle |= SS_NOPREFIX; } - ExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute."); + ThmExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute."); } else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) { // Parse the optional extended window style. hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle); - ExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute."); + ThmExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute."); hr = XmlGetAttribute(pixn, L"ImageList", &bstrText); if (S_FALSE != hr) { - ExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute."); + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute."); hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]); - ExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText); } hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText); if (S_FALSE != hr) { - ExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute."); + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute."); hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]); - ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText); } hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText); if (S_FALSE != hr) { - ExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute."); + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute."); hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]); - ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText); } hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText); if (S_FALSE != hr) { - ExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute."); + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute."); hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]); - ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText); } hr = ParseColumns(pixn, pControl); - ExitOnFailure(hr, "Failed to parse columns."); + ThmExitOnFailure(hr, "Failed to parse columns."); } else if (THEME_CONTROL_TYPE_PANEL == pControl->type) { hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); - ExitOnFailure(hr, "Failed to parse panel children."); + ThmExitOnFailure(hr, "Failed to parse panel children."); } else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type) { @@ -2827,12 +2841,12 @@ static HRESULT ParseControl( { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute."); + ThmExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute."); } else if (THEME_CONTROL_TYPE_TAB == pControl->type) { hr = ParseTabs(pixn, pControl); - ExitOnFailure(hr, "Failed to parse tabs"); + ThmExitOnFailure(hr, "Failed to parse tabs"); } else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type) { @@ -2847,7 +2861,7 @@ static HRESULT ParseControl( { pControl->dwStyle &= ~TVS_DISABLEDRAGDROP; } - ExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute."); + ThmExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute."); hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue); if (E_NOTFOUND == hr) @@ -2858,7 +2872,7 @@ static HRESULT ParseControl( { pControl->dwStyle |= TVS_FULLROWSELECT; } - ExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute."); + ThmExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute."); hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue); if (E_NOTFOUND == hr) @@ -2869,7 +2883,7 @@ static HRESULT ParseControl( { pControl->dwStyle |= TVS_HASBUTTONS; } - ExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute."); + ThmExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute."); hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue); if (E_NOTFOUND == hr) @@ -2880,7 +2894,7 @@ static HRESULT ParseControl( { pControl->dwStyle |= TVS_SHOWSELALWAYS; } - ExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute."); + ThmExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute."); hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue); if (E_NOTFOUND == hr) @@ -2891,7 +2905,7 @@ static HRESULT ParseControl( { pControl->dwStyle |= TVS_LINESATROOT; } - ExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute."); + ThmExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute."); hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue); if (E_NOTFOUND == hr) @@ -2902,7 +2916,7 @@ static HRESULT ParseControl( { pControl->dwStyle |= TVS_HASLINES; } - ExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute."); + ThmExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute."); } LExit: @@ -2924,15 +2938,15 @@ static HRESULT ParseActions( BSTR bstrType = NULL; hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl); - ExitOnFailure(hr, "Failed to select child action nodes."); + ThmExitOnFailure(hr, "Failed to select child action nodes."); hr = pixnl->get_length(reinterpret_cast(&pControl->cActions)); - ExitOnFailure(hr, "Failed to count the number of action nodes."); + ThmExitOnFailure(hr, "Failed to count the number of action nodes."); if (0 < pControl->cActions) { MemAllocArray(reinterpret_cast(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions); - ExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs."); + ThmExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs."); i = 0; while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType))) @@ -2940,7 +2954,7 @@ static HRESULT ParseActions( if (!bstrType) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Null element encountered!"); + ThmExitOnFailure(hr, "Null element encountered!"); } THEME_ACTION* pAction = pControl->rgActions + i; @@ -2950,19 +2964,19 @@ static HRESULT ParseActions( pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY; hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName); - ExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute."); + ThmExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1)) { pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE; hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName); - ExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute."); + ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute."); hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute."); + ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute."); } } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1)) @@ -2972,13 +2986,13 @@ static HRESULT ParseActions( else { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType); + ThmExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType); } hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType); + ThmExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType); } if (!pAction->sczCondition) @@ -2986,7 +3000,7 @@ static HRESULT ParseActions( if (pControl->pDefaultAction) { hr = E_INVALIDDATA; - ExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName); + ThmExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName); } pControl->pDefaultAction = pAction; @@ -3019,38 +3033,38 @@ static HRESULT ParseColumns( BSTR bstrText = NULL; hr = XmlSelectNodes(pixn, L"Column", &pixnl); - ExitOnFailure(hr, "Failed to select child column nodes."); + ThmExitOnFailure(hr, "Failed to select child column nodes."); hr = pixnl->get_length(reinterpret_cast(&pControl->cColumns)); - ExitOnFailure(hr, "Failed to count the number of control columns."); + ThmExitOnFailure(hr, "Failed to count the number of control columns."); if (0 < pControl->cColumns) { hr = MemAllocArray(reinterpret_cast(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns); - ExitOnFailure(hr, "Failed to allocate column structs."); + ThmExitOnFailure(hr, "Failed to allocate column structs."); i = 0; while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) { hr = XmlGetText(pixnChild, &bstrText); - ExitOnFailure(hr, "Failed to get inner text of column element."); + ThmExitOnFailure(hr, "Failed to get inner text of column element."); hr = XmlGetAttributeNumber(pixnChild, L"Width", reinterpret_cast(&pControl->ptcColumns[i].nBaseWidth)); if (S_FALSE == hr) { pControl->ptcColumns[i].nBaseWidth = 100; } - ExitOnFailure(hr, "Failed to get column width attribute."); + ThmExitOnFailure(hr, "Failed to get column width attribute."); hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pControl->ptcColumns[i].fExpands)); if (E_NOTFOUND == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get expands attribute."); + ThmExitOnFailure(hr, "Failed to get expands attribute."); hr = StrAllocString(&(pControl->ptcColumns[i].pszName), bstrText, 0); - ExitOnFailure(hr, "Failed to copy column name."); + ThmExitOnFailure(hr, "Failed to copy column name."); ++i; ReleaseNullBSTR(bstrText); @@ -3092,7 +3106,7 @@ static HRESULT ParseRadioButtons( GetControls(pTheme, pParentControl, &pcControls, &prgControls); hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons); - ExitOnFailure(hr, "Failed to select RadioButtons nodes."); + ThmExitOnFailure(hr, "Failed to select RadioButtons nodes."); while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL))) { @@ -3101,13 +3115,13 @@ static HRESULT ParseRadioButtons( { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying RadioButtons Name."); + ThmExitOnFailure(hr, "Failed when querying RadioButtons Name."); hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl); - ExitOnFailure(hr, "Failed to select RadioButton nodes."); + ThmExitOnFailure(hr, "Failed to select RadioButton nodes."); hr = pixnl->get_length(reinterpret_cast(&cRadioButtons)); - ExitOnFailure(hr, "Failed to count the number of RadioButton nodes."); + ThmExitOnFailure(hr, "Failed to count the number of RadioButton nodes."); if (cRadioButtons) { @@ -3118,7 +3132,7 @@ static HRESULT ParseRadioButtons( } hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons); - ExitOnFailure(hr, "Failed to reallocate theme controls."); + ThmExitOnFailure(hr, "Failed to reallocate theme controls."); iControl = *pcControls; *pcControls += cRadioButtons; @@ -3131,7 +3145,7 @@ static HRESULT ParseRadioButtons( pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON; hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage); - ExitOnFailure(hr, "Failed to parse control."); + ThmExitOnFailure(hr, "Failed to parse control."); if (fFirst) { @@ -3140,7 +3154,7 @@ static HRESULT ParseRadioButtons( } hr = StrAllocString(&pControl->sczVariable, sczName, 0); - ExitOnFailure(hr, "Failed to copy radio button variable."); + ThmExitOnFailure(hr, "Failed to copy radio button variable."); if (pPage) { @@ -3181,24 +3195,24 @@ static HRESULT ParseTabs( BSTR bstrText = NULL; hr = XmlSelectNodes(pixn, L"Tab", &pixnl); - ExitOnFailure(hr, "Failed to select child tab nodes."); + ThmExitOnFailure(hr, "Failed to select child tab nodes."); hr = pixnl->get_length(reinterpret_cast(&pControl->cTabs)); - ExitOnFailure(hr, "Failed to count the number of tabs."); + ThmExitOnFailure(hr, "Failed to count the number of tabs."); if (0 < pControl->cTabs) { hr = MemAllocArray(reinterpret_cast(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs); - ExitOnFailure(hr, "Failed to allocate tab structs."); + ThmExitOnFailure(hr, "Failed to allocate tab structs."); i = 0; while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) { hr = XmlGetText(pixnChild, &bstrText); - ExitOnFailure(hr, "Failed to get inner text of tab element."); + ThmExitOnFailure(hr, "Failed to get inner text of tab element."); hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0); - ExitOnFailure(hr, "Failed to copy tab name."); + ThmExitOnFailure(hr, "Failed to copy tab name."); ++i; ReleaseNullBSTR(bstrText); @@ -3227,17 +3241,17 @@ static HRESULT ParseText( BSTR bstrText = NULL; hr = XmlSelectNodes(pixn, L"Text", &pixnl); - ExitOnFailure(hr, "Failed to select child Text nodes."); + ThmExitOnFailure(hr, "Failed to select child Text nodes."); hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalText)); - ExitOnFailure(hr, "Failed to count the number of Text nodes."); + ThmExitOnFailure(hr, "Failed to count the number of Text nodes."); *pfAnyChildren |= 0 < pControl->cConditionalText; if (0 < pControl->cConditionalText) { MemAllocArray(reinterpret_cast(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText); - ExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs."); + ThmExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs."); i = 0; while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) @@ -3249,17 +3263,17 @@ static HRESULT ParseText( { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying Text/@Condition attribute."); + ThmExitOnFailure(hr, "Failed when querying Text/@Condition attribute."); hr = XmlGetText(pixnChild, &bstrText); - ExitOnFailure(hr, "Failed to get inner text of Text element."); + ThmExitOnFailure(hr, "Failed to get inner text of Text element."); if (S_OK == hr) { if (pConditionalText->sczCondition) { hr = StrAllocString(&pConditionalText->sczText, bstrText, 0); - ExitOnFailure(hr, "Failed to copy text to conditional text."); + ThmExitOnFailure(hr, "Failed to copy text to conditional text."); ++i; } @@ -3268,11 +3282,11 @@ static HRESULT ParseText( if (pControl->sczText) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName); + ThmExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName); } hr = StrAllocString(&pControl->sczText, bstrText, 0); - ExitOnFailure(hr, "Failed to copy text to control."); + ThmExitOnFailure(hr, "Failed to copy text to control."); // Unconditional text entries aren't stored in the conditional text list. --pControl->cConditionalText; @@ -3303,19 +3317,19 @@ static HRESULT ParseTooltips( BSTR bstrText = NULL; hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild); - ExitOnFailure(hr, "Failed to select child Tooltip node."); + ThmExitOnFailure(hr, "Failed to select child Tooltip node."); if (S_OK == hr) { *pfAnyChildren |= TRUE; hr = XmlGetText(pixnChild, &bstrText); - ExitOnFailure(hr, "Failed to get inner text of Tooltip element."); + ThmExitOnFailure(hr, "Failed to get inner text of Tooltip element."); if (S_OK == hr) { hr = StrAllocString(&pControl->sczTooltip, bstrText, 0); - ExitOnFailure(hr, "Failed to copy tooltip text to control."); + ThmExitOnFailure(hr, "Failed to copy tooltip text to control."); } } @@ -3340,10 +3354,10 @@ static HRESULT ParseNotes( BSTR bstrText = NULL; hr = XmlSelectNodes(pixn, L"Note", &pixnl); - ExitOnFailure(hr, "Failed to select child Note nodes."); + ThmExitOnFailure(hr, "Failed to select child Note nodes."); hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalNotes)); - ExitOnFailure(hr, "Failed to count the number of Note nodes."); + ThmExitOnFailure(hr, "Failed to count the number of Note nodes."); if (pfAnyChildren) { @@ -3353,7 +3367,7 @@ static HRESULT ParseNotes( if (0 < pControl->cConditionalNotes) { MemAllocArray(reinterpret_cast(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes); - ExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs."); + ThmExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs."); i = 0; while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) @@ -3365,17 +3379,17 @@ static HRESULT ParseNotes( { hr = S_OK; } - ExitOnFailure(hr, "Failed when querying Note/@Condition attribute."); + ThmExitOnFailure(hr, "Failed when querying Note/@Condition attribute."); hr = XmlGetText(pixnChild, &bstrText); - ExitOnFailure(hr, "Failed to get inner text of Note element."); + ThmExitOnFailure(hr, "Failed to get inner text of Note element."); if (S_OK == hr) { if (pConditionalNote->sczCondition) { hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0); - ExitOnFailure(hr, "Failed to copy text to conditional note text."); + ThmExitOnFailure(hr, "Failed to copy text to conditional note text."); ++i; } @@ -3384,11 +3398,11 @@ static HRESULT ParseNotes( if (pControl->sczNote) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName); + ThmExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName); } hr = StrAllocString(&pControl->sczNote, bstrText, 0); - ExitOnFailure(hr, "Failed to copy text to command link control."); + ThmExitOnFailure(hr, "Failed to copy text to command link control."); // Unconditional note entries aren't stored in the conditional notes list. --pControl->cConditionalNotes; @@ -3427,7 +3441,7 @@ static HRESULT StartBillboard( if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL)) { - ExitWithLastError(hr, "Failed to start billboard."); + ThmExitWithLastError(hr, "Failed to start billboard."); } hr = S_OK; @@ -3865,7 +3879,7 @@ static DWORD CALLBACK RichEditStreamFromFileHandleCallback( if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast(pcb), NULL)) { - ExitWithLastError(hr, "Failed to read file"); + ThmExitWithLastError(hr, "Failed to read file"); } LExit: @@ -3967,17 +3981,17 @@ static void OnBrowseDirectory( if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality) { hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); - ExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName); + ThmExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName); } else if (pTheme->pfnSetStringVariable) { hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); + ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); } else if (pTargetControl) { hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); - ExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName); + ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName); } ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); @@ -4019,7 +4033,7 @@ static BOOL OnButtonClicked( BOOL fCondition = FALSE; hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition); + ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition); if (fCondition) { @@ -4049,7 +4063,7 @@ static BOOL OnButtonClicked( if (!dwPageId) { - ExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName); + ThmExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName); } ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT); @@ -4108,7 +4122,7 @@ static HRESULT OnRichEditEnLink( case WM_LBUTTONDOWN: { hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2); - ExitOnFailure(hr, "Failed to allocate string for link."); + ThmExitOnFailure(hr, "Failed to allocate string for link."); TEXTRANGEW tr; tr.chrg.cpMin = link->chrg.cpMin; @@ -4118,7 +4132,7 @@ static HRESULT OnRichEditEnLink( if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast(&tr))) { hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); - ExitOnFailure(hr, "Failed to launch link: %ls", sczLink); + ThmExitOnFailure(hr, "Failed to launch link: %ls", sczLink); } break; @@ -4209,7 +4223,7 @@ static HRESULT SizeListViewColumns( if (!::GetWindowRect(pControl->hWnd, &rcParent)) { - ExitWithLastError(hr, "Failed to get window rect of listview control."); + ThmExitWithLastError(hr, "Failed to get window rect of listview control."); } iExtraAvailableSize = rcParent.right - rcParent.left; @@ -4270,10 +4284,10 @@ static HRESULT ShowControl( fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) { hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText); - ExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); + ThmExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); } HWND hWnd = pControl->hWnd; @@ -4301,14 +4315,14 @@ static HRESULT ShowControl( if (pControl->sczVisibleCondition) { hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); } // If the control has an EnableCondition, check if it's true. if (pControl->sczEnableCondition) { hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); } } @@ -4332,7 +4346,7 @@ static HRESULT ShowControl( BOOL fCondition = FALSE; hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition); + ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition); if (fCondition) { @@ -4352,7 +4366,7 @@ static HRESULT ShowControl( BOOL fCondition = FALSE; hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition); + ThmExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition); if (fCondition) { @@ -4366,7 +4380,7 @@ static HRESULT ShowControl( if (wzText && *wzText) { hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to format string: %ls", wzText); + ThmExitOnFailure(hr, "Failed to format string: %ls", wzText); } else { @@ -4378,7 +4392,7 @@ static HRESULT ShowControl( if (wzNote && *wzNote) { hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to format note: %ls", wzNote); + ThmExitOnFailure(hr, "Failed to format note: %ls", wzNote); } else { @@ -4401,7 +4415,7 @@ static HRESULT ShowControl( { hr = S_OK; } - ExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName); if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) { @@ -4411,7 +4425,7 @@ static HRESULT ShowControl( if (SUCCEEDED(hr)) { hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue); - ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); } ++iPageControl; @@ -4431,7 +4445,7 @@ static HRESULT ShowControl( } else { - ExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczName); } if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) @@ -4442,7 +4456,7 @@ static HRESULT ShowControl( if (SUCCEEDED(hr)) { hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); - ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); } ++iPageControl; @@ -4463,7 +4477,7 @@ static HRESULT ShowControl( } else { - ExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable); + ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable); } if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton) @@ -4474,7 +4488,7 @@ static HRESULT ShowControl( if (SUCCEEDED(hr)) { hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); - ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable); } ++iPageControl; @@ -4552,7 +4566,7 @@ static HRESULT ShowControls( if (!pControl->wPageId || pControl->wPageId == dwPageId) { hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus); - ExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i); + ThmExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i); } } @@ -4702,7 +4716,7 @@ static HRESULT LoadControls( else { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Invalid image or image list coordinates."); + ThmExitOnRootFailure(hr, "Invalid image or image list coordinates."); } break; @@ -4746,7 +4760,7 @@ static HRESULT LoadControls( if (!vhModuleRichEd) { hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); - ExitOnFailure(hr, "Failed to load Rich Edit control library."); + ThmExitOnFailure(hr, "Failed to load Rich Edit control library."); } wzWindowClass = RICHEDIT_CLASSW; dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY; @@ -4765,7 +4779,7 @@ static HRESULT LoadControls( wzWindowClass = WC_TREEVIEWW; break; } - ExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type); + ThmExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type); // Default control ids to the theme id and its index in the control array, unless there // is a specific id to assign to a named control. @@ -4791,7 +4805,7 @@ static HRESULT LoadControls( if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) { hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); if (!fVisible) { @@ -4812,7 +4826,7 @@ static HRESULT LoadControls( BOOL fEnable = TRUE; hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext); - ExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); fDisabled = !fEnable; dwWindowBits |= fDisabled ? WS_DISABLED : 0; @@ -4826,7 +4840,7 @@ static HRESULT LoadControls( } pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pTheme); - ExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); + ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); if (pControl->sczTooltip) { @@ -4875,7 +4889,7 @@ static HRESULT LoadControls( ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle); hr = SizeListViewColumns(pControl); - ExitOnFailure(hr, "Failed to get size of list view columns."); + ThmExitOnFailure(hr, "Failed to get size of list view columns."); for (DWORD j = 0; j < pControl->cColumns; ++j) { @@ -4889,7 +4903,7 @@ static HRESULT LoadControls( if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc))) { - ExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j); + ThmExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j); } // Return value tells us the old image list, we don't care. @@ -4938,7 +4952,7 @@ static HRESULT LoadControls( if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci))) { - ExitWithLastError(hr, "Failed to insert tab %u into tab control.", j); + ThmExitWithLastError(hr, "Failed to insert tab %u into tab control.", j); } } } @@ -4961,7 +4975,7 @@ static HRESULT LoadControls( if (pControl->cControls) { hr = LoadControls(pTheme, pControl, pControl->hWnd, rgAssignControlIds, cAssignControlIds); - ExitOnFailure(hr, "Failed to load child controls."); + ThmExitOnFailure(hr, "Failed to load child controls."); } } @@ -4983,7 +4997,7 @@ static HRESULT LocalizeControls( { THEME_CONTROL* pControl = rgControls + i; hr = LocalizeControl(pControl, pWixLoc); - ExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName); } LExit: @@ -5002,59 +5016,59 @@ static HRESULT LocalizeControl( if (pControl->sczText && *pControl->sczText) { hr = LocLocalizeString(pWixLoc, &pControl->sczText); - ExitOnFailure(hr, "Failed to localize control text."); + ThmExitOnFailure(hr, "Failed to localize control text."); } else if (pControl->sczName) { LOC_STRING* plocString = NULL; hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName); - ExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName); hr = LocGetString(pWixLoc, sczLocStringId, &plocString); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName); hr = StrAllocString(&pControl->sczText, plocString->wzText, 0); - ExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText); + ThmExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText); } } if (pControl->sczTooltip && *pControl->sczTooltip) { hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip); - ExitOnFailure(hr, "Failed to localize control tooltip text."); + ThmExitOnFailure(hr, "Failed to localize control tooltip text."); } if (pControl->sczNote && *pControl->sczNote) { hr = LocLocalizeString(pWixLoc, &pControl->sczNote); - ExitOnFailure(hr, "Failed to localize control note text."); + ThmExitOnFailure(hr, "Failed to localize control note text."); } for (DWORD j = 0; j < pControl->cConditionalText; ++j) { hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText); - ExitOnFailure(hr, "Failed to localize conditional text."); + ThmExitOnFailure(hr, "Failed to localize conditional text."); } for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) { hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText); - ExitOnFailure(hr, "Failed to localize conditional note."); + ThmExitOnFailure(hr, "Failed to localize conditional note."); } for (DWORD j = 0; j < pControl->cColumns; ++j) { hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName); - ExitOnFailure(hr, "Failed to localize column text."); + ThmExitOnFailure(hr, "Failed to localize column text."); } for (DWORD j = 0; j < pControl->cTabs; ++j) { hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName); - ExitOnFailure(hr, "Failed to localize tab text."); + ThmExitOnFailure(hr, "Failed to localize tab text."); } // Localize control's size, location, and text. @@ -5065,7 +5079,7 @@ static HRESULT LocalizeControl( { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to localize control."); + ThmExitOnFailure(hr, "Failed to localize control."); if (LOC_CONTROL_NOT_SET != pLocControl->nX) { @@ -5090,7 +5104,7 @@ static HRESULT LocalizeControl( if (pLocControl->wzText && *pLocControl->wzText) { hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0); - ExitOnFailure(hr, "Failed to localize control text."); + ThmExitOnFailure(hr, "Failed to localize control text."); } } @@ -5114,7 +5128,7 @@ static HRESULT LoadControlsString( { THEME_CONTROL* pControl = rgControls + i; hr = LoadControlString(pControl, hResModule); - ExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName); + ThmExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName); } LExit: @@ -5130,14 +5144,14 @@ static HRESULT LoadControlString( if (UINT_MAX != pControl->uStringId) { hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText); - ExitOnFailure(hr, "Failed to load control text."); + ThmExitOnFailure(hr, "Failed to load control text."); for (DWORD j = 0; j < pControl->cColumns; ++j) { if (UINT_MAX != pControl->ptcColumns[j].uStringId) { hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName); - ExitOnFailure(hr, "Failed to load column text."); + ThmExitOnFailure(hr, "Failed to load column text."); } } @@ -5146,7 +5160,7 @@ static HRESULT LoadControlString( if (UINT_MAX != pControl->pttTabs[j].uStringId) { hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName); - ExitOnFailure(hr, "Failed to load tab text."); + ThmExitOnFailure(hr, "Failed to load tab text."); } } } -- cgit v1.2.3-55-g6feb From 3afdaa298c9bd341c04472a739f2b28ac8c577ca Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 5 Jul 2020 17:05:23 +1000 Subject: Methods in thmtuil.h shouldn't be declared as EXTERN "C". --- src/dutil/inc/thmutil.h | 80 ++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index 5b3d4667..4d52fa69 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -299,7 +299,7 @@ struct THEME ThemeInitialize - initialized theme management. *******************************************************************/ -DAPI_(HRESULT) ThemeInitialize( +HRESULT DAPI ThemeInitialize( __in_opt HMODULE hModule ); @@ -307,13 +307,13 @@ DAPI_(HRESULT) ThemeInitialize( ThemeUninitialize - uninitialize theme management. *******************************************************************/ -DAPI_(void) ThemeUninitialize(); +void DAPI ThemeUninitialize(); /******************************************************************** ThemeLoadFromFile - loads a theme from a loose file. *******************************************************************/ -DAPI_(HRESULT) ThemeLoadFromFile( +HRESULT DAPI ThemeLoadFromFile( __in_z LPCWSTR wzThemeFile, __out THEME** ppTheme ); @@ -323,7 +323,7 @@ DAPI_(HRESULT) ThemeLoadFromFile( NOTE: The resource data must be UTF-8 encoded. *******************************************************************/ -DAPI_(HRESULT) ThemeLoadFromResource( +HRESULT DAPI ThemeLoadFromResource( __in_opt HMODULE hModule, __in_z LPCSTR szResource, __out THEME** ppTheme @@ -333,7 +333,7 @@ DAPI_(HRESULT) ThemeLoadFromResource( ThemeFree - frees any memory associated with a theme. *******************************************************************/ -DAPI_(void) ThemeFree( +void DAPI ThemeFree( __in THEME* pTheme ); @@ -342,7 +342,7 @@ ThemeRegisterVariableCallbacks - registers a context and callbacks for working with variables. *******************************************************************/ -DAPI_(HRESULT) ThemeRegisterVariableCallbacks( +HRESULT DAPI ThemeRegisterVariableCallbacks( __in THEME* pTheme, __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, @@ -357,7 +357,7 @@ DAPI_(HRESULT) ThemeRegisterVariableCallbacks( ThemeLoadControls - creates the windows for all the theme controls. *******************************************************************/ -DAPI_(HRESULT) ThemeLoadControls( +HRESULT DAPI ThemeLoadControls( __in THEME* pTheme, __in HWND hwndParent, __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, @@ -369,7 +369,7 @@ DAPI_(HRESULT) ThemeLoadControls( controls can be reloaded. *******************************************************************/ -DAPI_(void) ThemeUnloadControls( +void DAPI ThemeUnloadControls( __in THEME* pTheme ); @@ -377,12 +377,12 @@ DAPI_(void) ThemeUnloadControls( ThemeLocalize - Localizes all of the strings in the theme. *******************************************************************/ -DAPI_(HRESULT) ThemeLocalize( +HRESULT DAPI ThemeLocalize( __in THEME *pTheme, __in const WIX_LOCALIZATION *pLocStringSet ); -DAPI_(HRESULT) ThemeLoadStrings( +HRESULT DAPI ThemeLoadStrings( __in THEME* pTheme, __in HMODULE hResModule ); @@ -391,7 +391,7 @@ DAPI_(HRESULT) ThemeLoadStrings( ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file. *******************************************************************/ -DAPI_(HRESULT) ThemeLoadRichEditFromFile( +HRESULT DAPI ThemeLoadRichEditFromFile( __in THEME* pTheme, __in DWORD dwControl, __in_z LPCWSTR wzFileName, @@ -402,7 +402,7 @@ DAPI_(HRESULT) ThemeLoadRichEditFromFile( ThemeLoadRichEditFromResource - Attach a richedit control to resource data. *******************************************************************/ -DAPI_(HRESULT) ThemeLoadRichEditFromResource( +HRESULT DAPI ThemeLoadRichEditFromResource( __in THEME* pTheme, __in DWORD dwControl, __in_z LPCSTR szResourceName, @@ -414,7 +414,7 @@ DAPI_(HRESULT) ThemeLoadRichEditFromResource( HWND) to resource data. *******************************************************************/ -DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( +HRESULT DAPI ThemeLoadRichEditFromResourceToHWnd( __in HWND hWnd, __in_z LPCSTR szResourceName, __in HMODULE hModule @@ -425,7 +425,7 @@ DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( accelerator table. *******************************************************************/ -DAPI_(BOOL) ThemeHandleKeyboardMessage( +BOOL DAPI ThemeHandleKeyboardMessage( __in_opt THEME* pTheme, __in HWND hWnd, __in MSG* pMsg @@ -447,7 +447,7 @@ LRESULT CALLBACK ThemeDefWindowProc( ThemeGetPageIds - gets the page ids for the theme via page names. *******************************************************************/ -DAPI_(void) ThemeGetPageIds( +void DAPI ThemeGetPageIds( __in const THEME* pTheme, __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, __inout_ecount(cGetPages) DWORD* rgdwPageIds, @@ -458,7 +458,7 @@ DAPI_(void) ThemeGetPageIds( ThemeGetPage - gets a theme page by id. *******************************************************************/ -DAPI_(THEME_PAGE*) ThemeGetPage( +THEME_PAGE* DAPI ThemeGetPage( __in const THEME* pTheme, __in DWORD dwPage ); @@ -467,7 +467,7 @@ DAPI_(THEME_PAGE*) ThemeGetPage( ThemeShowPage - shows or hides all of the controls in the page at one time. *******************************************************************/ -DAPI_(HRESULT) ThemeShowPage( +HRESULT DAPI ThemeShowPage( __in THEME* pTheme, __in DWORD dwPage, __in int nCmdShow @@ -481,7 +481,7 @@ ThemeShowPageEx - shows or hides all of the controls in the page at one time. showing a new page. *******************************************************************/ -DAPI_(HRESULT) ThemeShowPageEx( +HRESULT DAPI ThemeShowPageEx( __in THEME* pTheme, __in DWORD dwPage, __in int nCmdShow, @@ -493,7 +493,7 @@ DAPI_(HRESULT) ThemeShowPageEx( ThemeShowChild - shows a control's specified child control, hiding the rest. *******************************************************************/ -DAPI_(void) ThemeShowChild( +void DAPI ThemeShowChild( __in THEME* pTheme, __in THEME_CONTROL* pParentControl, __in DWORD dwIndex @@ -503,7 +503,7 @@ DAPI_(void) ThemeShowChild( ThemeControlExists - check if a control with the specified id exists. *******************************************************************/ -DAPI_(BOOL) ThemeControlExists( +BOOL DAPI ThemeControlExists( __in const THEME* pTheme, __in DWORD dwControl ); @@ -512,7 +512,7 @@ DAPI_(BOOL) ThemeControlExists( ThemeControlEnable - enables/disables a control. *******************************************************************/ -DAPI_(void) ThemeControlEnable( +void DAPI ThemeControlEnable( __in THEME* pTheme, __in DWORD dwControl, __in BOOL fEnable @@ -522,7 +522,7 @@ DAPI_(void) ThemeControlEnable( ThemeControlEnabled - returns whether a control is enabled/disabled. *******************************************************************/ -DAPI_(BOOL) ThemeControlEnabled( +BOOL DAPI ThemeControlEnabled( __in THEME* pTheme, __in DWORD dwControl ); @@ -531,7 +531,7 @@ DAPI_(BOOL) ThemeControlEnabled( ThemeControlElevates - sets/removes the shield icon on a control. *******************************************************************/ -DAPI_(void) ThemeControlElevates( +void DAPI ThemeControlElevates( __in THEME* pTheme, __in DWORD dwControl, __in BOOL fElevates @@ -541,7 +541,7 @@ DAPI_(void) ThemeControlElevates( ThemeShowControl - shows/hides a control. *******************************************************************/ -DAPI_(void) ThemeShowControl( +void DAPI ThemeShowControl( __in THEME* pTheme, __in DWORD dwControl, __in int nCmdShow @@ -552,7 +552,7 @@ ThemeShowControlEx - shows/hides a control with support for conditional text and notes. *******************************************************************/ -DAPI_(void) ThemeShowControlEx( +void DAPI ThemeShowControlEx( __in THEME* pTheme, __in DWORD dwControl, __in int nCmdShow @@ -562,12 +562,12 @@ DAPI_(void) ThemeShowControlEx( ThemeControlVisible - returns whether a control is visible. *******************************************************************/ -DAPI_(BOOL) ThemeControlVisible( +BOOL DAPI ThemeControlVisible( __in THEME* pTheme, __in DWORD dwControl ); -DAPI_(BOOL) ThemePostControlMessage( +BOOL DAPI ThemePostControlMessage( __in THEME* pTheme, __in DWORD dwControl, __in UINT Msg, @@ -575,7 +575,7 @@ DAPI_(BOOL) ThemePostControlMessage( __in LPARAM lParam ); -DAPI_(LRESULT) ThemeSendControlMessage( +LRESULT DAPI ThemeSendControlMessage( __in const THEME* pTheme, __in DWORD dwControl, __in UINT Msg, @@ -587,7 +587,7 @@ DAPI_(LRESULT) ThemeSendControlMessage( ThemeDrawBackground - draws the theme background. *******************************************************************/ -DAPI_(HRESULT) ThemeDrawBackground( +HRESULT DAPI ThemeDrawBackground( __in THEME* pTheme, __in PAINTSTRUCT* pps ); @@ -596,7 +596,7 @@ DAPI_(HRESULT) ThemeDrawBackground( ThemeDrawControl - draw an owner drawn control. *******************************************************************/ -DAPI_(HRESULT) ThemeDrawControl( +HRESULT DAPI ThemeDrawControl( __in THEME* pTheme, __in DRAWITEMSTRUCT* pdis ); @@ -605,7 +605,7 @@ DAPI_(HRESULT) ThemeDrawControl( ThemeHoverControl - mark a control as hover. *******************************************************************/ -DAPI_(BOOL) ThemeHoverControl( +BOOL DAPI ThemeHoverControl( __in THEME* pTheme, __in HWND hwndParent, __in HWND hwndControl @@ -616,7 +616,7 @@ DAPI_(BOOL) ThemeHoverControl( really useful for checkbox controls. *******************************************************************/ -DAPI_(BOOL) ThemeIsControlChecked( +BOOL DAPI ThemeIsControlChecked( __in THEME* pTheme, __in DWORD dwControl ); @@ -625,7 +625,7 @@ DAPI_(BOOL) ThemeIsControlChecked( ThemeSetControlColor - sets the color of text for a control. *******************************************************************/ -DAPI_(BOOL) ThemeSetControlColor( +BOOL DAPI ThemeSetControlColor( __in THEME* pTheme, __in HDC hdc, __in HWND hWnd, @@ -637,7 +637,7 @@ DAPI_(BOOL) ThemeSetControlColor( progress bar control. *******************************************************************/ -DAPI_(HRESULT) ThemeSetProgressControl( +HRESULT DAPI ThemeSetProgressControl( __in THEME* pTheme, __in DWORD dwControl, __in DWORD dwProgressPercentage @@ -648,7 +648,7 @@ DAPI_(HRESULT) ThemeSetProgressControl( progress bar control. *******************************************************************/ -DAPI_(HRESULT) ThemeSetProgressControlColor( +HRESULT DAPI ThemeSetProgressControlColor( __in THEME* pTheme, __in DWORD dwControl, __in DWORD dwColorIndex @@ -658,7 +658,7 @@ DAPI_(HRESULT) ThemeSetProgressControlColor( ThemeSetTextControl - sets the text of a control. *******************************************************************/ -DAPI_(HRESULT) ThemeSetTextControl( +HRESULT DAPI ThemeSetTextControl( __in const THEME* pTheme, __in DWORD dwControl, __in_z_opt LPCWSTR wzText @@ -669,7 +669,7 @@ ThemeSetTextControl - sets the text of a control and optionally invalidates the control. *******************************************************************/ -DAPI_(HRESULT) ThemeSetTextControlEx( +HRESULT DAPI ThemeSetTextControlEx( __in const THEME* pTheme, __in DWORD dwControl, __in BOOL fUpdate, @@ -680,7 +680,7 @@ DAPI_(HRESULT) ThemeSetTextControlEx( ThemeGetTextControl - gets the text of a control. *******************************************************************/ -DAPI_(HRESULT) ThemeGetTextControl( +HRESULT DAPI ThemeGetTextControl( __in const THEME* pTheme, __in DWORD dwControl, __out_z LPWSTR* psczText @@ -690,7 +690,7 @@ DAPI_(HRESULT) ThemeGetTextControl( ThemeUpdateCaption - updates the caption in the theme. *******************************************************************/ -DAPI_(HRESULT) ThemeUpdateCaption( +HRESULT DAPI ThemeUpdateCaption( __in THEME* pTheme, __in_z LPCWSTR wzCaption ); @@ -700,7 +700,7 @@ DAPI_(HRESULT) ThemeUpdateCaption( enabled control if it is disabled. *******************************************************************/ -DAPI_(void) ThemeSetFocus( +void DAPI ThemeSetFocus( __in THEME* pTheme, __in DWORD dwControl ); -- cgit v1.2.3-55-g6feb From e586152b76d62dbf38cd2882819e23eee2e0af7f Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 5 Jul 2020 17:15:43 +1000 Subject: Add ThemeCreateParentWindow so thmutil controls its window creation. --- src/dutil/inc/thmutil.h | 22 +++++++++++-- src/dutil/thmutil.cpp | 82 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index 4d52fa69..e661a6cb 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -354,12 +354,30 @@ HRESULT DAPI ThemeRegisterVariableCallbacks( ); /******************************************************************** - ThemeLoadControls - creates the windows for all the theme controls. + ThemeCreateParentWindow - creates a parent window for the theme. + +*******************************************************************/ +HRESULT DAPI ThemeCreateParentWindow( + __in THEME* pTheme, + __in DWORD dwExStyle, + __in LPCWSTR szClassName, + __in LPCWSTR szWindowName, + __in DWORD dwStyle, + __in int x, + __in int y, + __in_opt HWND hwndParent, + __in_opt HINSTANCE hInstance, + __in_opt LPVOID lpParam, + __out_opt HWND* phWnd + ); + +/******************************************************************** + ThemeLoadControls - creates the windows for all the theme controls + using the window created in ThemeCreateParentWindow. *******************************************************************/ HRESULT DAPI ThemeLoadControls( __in THEME* pTheme, - __in HWND hwndParent, __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, __in DWORD cAssignControlIds ); diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 1939c64d..2050b420 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -17,6 +17,7 @@ #define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__) #define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__) +// from CommCtrl.h #ifndef BS_COMMANDLINK #define BS_COMMANDLINK 0x0000000EL #endif @@ -184,7 +185,6 @@ static HRESULT FindImageList( static HRESULT LoadControls( __in THEME* pTheme, __in_opt THEME_CONTROL* pParentControl, - __in HWND hwndParent, __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, __in DWORD cAssignControlIds ); @@ -287,6 +287,11 @@ static BOOL OnButtonClicked( __in HWND hWnd, __in const THEME_CONTROL* pControl ); +static void OnNcCreate( + __in THEME* pTheme, + __in HWND hWnd, + __in LPARAM lParam + ); static HRESULT OnRichEditEnLink( __in LPARAM lParam, __in HWND hWndRichEdit, @@ -553,14 +558,60 @@ LExit: } +DAPI_(HRESULT) ThemeCreateParentWindow( + __in THEME* pTheme, + __in DWORD dwExStyle, + __in LPCWSTR szClassName, + __in LPCWSTR szWindowName, + __in DWORD dwStyle, + __in int x, + __in int y, + __in_opt HWND hwndParent, + __in_opt HINSTANCE hInstance, + __in_opt LPVOID lpParam, + __out_opt HWND* phWnd + ) +{ + HRESULT hr = S_OK; + HWND hWnd = NULL; + + if (pTheme->hwndParent) + { + ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded."); + } + + hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWidth, pTheme->nHeight, hwndParent, NULL, hInstance, lpParam); + ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window."); + ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE."); + AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window."); + + if (phWnd) + { + *phWnd = hWnd; + } + +LExit: + return hr; +} + + DAPI_(HRESULT) ThemeLoadControls( __in THEME* pTheme, - __in HWND hwndParent, __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, __in DWORD cAssignControlIds ) { - return LoadControls(pTheme, NULL, hwndParent, rgAssignControlIds, cAssignControlIds); + HRESULT hr = S_OK; + + if (!pTheme->hwndParent) + { + ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created."); + } + + hr = LoadControls(pTheme, NULL, rgAssignControlIds, cAssignControlIds); + +LExit: + return hr; } @@ -735,6 +786,10 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( { switch (uMsg) { + case WM_NCCREATE: + OnNcCreate(pTheme, hWnd, lParam); + break; + case WM_NCHITTEST: if (pTheme->dwStyle & WS_POPUP) { @@ -4107,6 +4162,15 @@ LExit: return fHandled; } +static void OnNcCreate( + __in THEME* pTheme, + __in HWND hWnd, + __in LPARAM /*lParam*/ + ) +{ + pTheme->hwndParent = hWnd; +} + static HRESULT OnRichEditEnLink( __in LPARAM lParam, __in HWND hWndRichEdit, @@ -4620,7 +4684,6 @@ static LRESULT CALLBACK PanelWndProc( static HRESULT LoadControls( __in THEME* pTheme, __in_opt THEME_CONTROL* pParentControl, - __in HWND hwndParent, __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, __in DWORD cAssignControlIds ) @@ -4631,15 +4694,10 @@ static HRESULT LoadControls( BOOL fStartNewGroup = FALSE; DWORD cControls = 0; THEME_CONTROL* rgControls = NULL; - - if (!pParentControl) - { - AssertSz(!pTheme->hwndParent, "Theme already loaded controls because it has a parent window."); - pTheme->hwndParent = hwndParent; - } + HWND hwndParent = pParentControl ? pParentControl->hWnd : pTheme->hwndParent; GetControls(pTheme, pParentControl, cControls, rgControls); - ::GetClientRect(pParentControl ? pParentControl->hWnd : pTheme->hwndParent, &rcParent); + ::GetClientRect(hwndParent, &rcParent); for (DWORD i = 0; i < cControls; ++i) { @@ -4974,7 +5032,7 @@ static HRESULT LoadControls( if (pControl->cControls) { - hr = LoadControls(pTheme, pControl, pControl->hWnd, rgAssignControlIds, cAssignControlIds); + hr = LoadControls(pTheme, pControl, rgAssignControlIds, cAssignControlIds); ThmExitOnFailure(hr, "Failed to load child controls."); } } -- cgit v1.2.3-55-g6feb From a44cba1e241d0aa7d5c64595e9e7c95d0f06cced Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 16:03:23 +1000 Subject: Start High-DPI support by scaling the parent window according to the DPI. --- src/dutil/dpiutil.cpp | 117 ++++++++++++++++++++++++++++++++++++++++ src/dutil/dutil.vcxproj | 2 + src/dutil/dutil.vcxproj.filters | 15 ++++-- src/dutil/inc/dpiutil.h | 55 +++++++++++++++++++ src/dutil/inc/dutilsources.h | 1 + src/dutil/inc/thmutil.h | 6 +++ src/dutil/precomp.h | 2 + src/dutil/thmutil.cpp | 90 ++++++++++++++++++++++++++++--- 8 files changed, 277 insertions(+), 11 deletions(-) create mode 100644 src/dutil/dpiutil.cpp create mode 100644 src/dutil/inc/dpiutil.h diff --git a/src/dutil/dpiutil.cpp b/src/dutil/dpiutil.cpp new file mode 100644 index 00000000..edab8f01 --- /dev/null +++ b/src/dutil/dpiutil.cpp @@ -0,0 +1,117 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__) + +static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; +static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; + +static HMODULE vhShcoreDll = NULL; +static HMODULE vhUser32Dll = NULL; +static BOOL vfDpiuInitialized = FALSE; + +DAPI_(void) DpiuInitialize() +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnGetDpiForMonitor = reinterpret_cast(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor")); + } + + hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); + } + + vfDpiuInitialized = TRUE; +} + +DAPI_(void) DpiuUninitialize() +{ + if (vhShcoreDll) + { + ::FreeLibrary(vhShcoreDll); + } + + if (vhUser32Dll) + { + ::FreeLibrary(vhUser32Dll); + } + + vhShcoreDll = NULL; + vhUser32Dll = NULL; + vpfnGetDpiForMonitor = NULL; + vpfnGetDpiForWindow = NULL; + vfDpiuInitialized = FALSE; +} + +DAPI_(void) DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ) +{ + HRESULT hr = S_OK; + HMONITOR hMonitor = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + HDC hdc = NULL; + + pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI; + + if (vpfnGetDpiForWindow) + { + pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd); + ExitFunction(); + } + + if (vpfnGetDpiForMonitor) + { + hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + if (SUCCEEDED(hr)) + { + pWindowContext->nDpi = dpiX; + ExitFunction(); + } + } + } + + hdc = ::GetDC(hWnd); + if (hdc) + { + pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); + } + +LExit: + if (hdc) + { + ::ReleaseDC(hWnd, hdc); + } +} + +DAPI_(int) DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ) +{ + return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI); +} diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index af8385d1..4bae04d6 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -73,6 +73,7 @@ + Create 4091;4458 @@ -139,6 +140,7 @@ + diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters index 4dd90fdd..01dd6661 100644 --- a/src/dutil/dutil.vcxproj.filters +++ b/src/dutil/dutil.vcxproj.filters @@ -60,6 +60,9 @@ Source Files + + Source Files + Source Files @@ -230,9 +233,15 @@ Header Files + + Header Files + Header Files + + Header Files + Header Files @@ -287,6 +296,9 @@ Header Files + + Header Files + Header Files @@ -344,9 +356,6 @@ Header Files - - Header Files - diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h new file mode 100644 index 00000000..c6f73b02 --- /dev/null +++ b/src/dutil/inc/dpiutil.h @@ -0,0 +1,55 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// from WinUser.h +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif +#ifndef USER_DEFAULT_SCREEN_DPI +#define USER_DEFAULT_SCREEN_DPI 96 +#endif + +typedef struct _DPIU_WINDOW_CONTEXT +{ + UINT nDpi; +} DPIU_WINDOW_CONTEXT; + +typedef HRESULT (APIENTRY *PFN_GETDPIFORMONITOR)( + __in HMONITOR hmonitor, + __in MONITOR_DPI_TYPE dpiType, + __in UINT* dpiX, + __in UINT* dpiY + ); +typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( + __in HWND hwnd + ); + +void DAPI DpiuInitialize(); +void DAPI DpiuUninitialize(); + +/******************************************************************** + DpiuGetWindowContext - get the DPI context of the given window. + +*******************************************************************/ +void DAPI DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ); + +/******************************************************************** + DpiuScaleValue - scale the value to the target DPI. + +*******************************************************************/ +int DAPI DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h index c88ada37..bf3da16f 100644 --- a/src/dutil/inc/dutilsources.h +++ b/src/dutil/inc/dutilsources.h @@ -19,6 +19,7 @@ typedef enum DUTIL_SOURCE DUTIL_SOURCE_DICTUTIL, DUTIL_SOURCE_DIRUTIL, DUTIL_SOURCE_DLUTIL, + DUTIL_SOURCE_DPIUTIL, DUTIL_SOURCE_DUTIL, DUTIL_SOURCE_ESEUTIL, DUTIL_SOURCE_FILEUTIL, diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index e661a6cb..52de755f 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -255,6 +255,10 @@ struct THEME DWORD dwFontId; HANDLE hIcon; LPWSTR sczCaption; + int nDefaultDpiHeight; + int nDefaultDpiMinimumHeight; + int nDefaultDpiWidth; + int nDefaultDpiMinimumWidth; int nHeight; int nMinimumHeight; int nWidth; @@ -283,6 +287,8 @@ struct THEME DWORD dwCurrentPageId; HWND hwndTooltip; + UINT nDpi; + // callback functions PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition; PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString; diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h index eebdd160..7fdc83ae 100644 --- a/src/dutil/precomp.h +++ b/src/dutil/precomp.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "dutilsources.h" #include "dutil.h" @@ -53,6 +54,7 @@ #include "eseutil.h" #include "dirutil.h" #include "dlutil.h" +#include "dpiutil.h" #include "fileutil.h" #include "guidutil.h" #include "gdiputil.h" diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 2050b420..4cc149c9 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -287,6 +287,11 @@ static BOOL OnButtonClicked( __in HWND hWnd, __in const THEME_CONTROL* pControl ); +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ); static void OnNcCreate( __in THEME* pTheme, __in HWND hWnd, @@ -353,6 +358,12 @@ static void ResizeControl( __in THEME_CONTROL* pControl, __in const RECT* prcParent ); +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ); static void GetControls( __in THEME* pTheme, __in_opt THEME_CONTROL* pParentControl, @@ -380,6 +391,8 @@ DAPI_(HRESULT) ThemeInitialize( HRESULT hr = S_OK; INITCOMMONCONTROLSEX icex = { }; + DpiuInitialize(); + hr = XmlInitialize(); ThmExitOnFailure(hr, "Failed to initialize XML."); @@ -430,6 +443,7 @@ DAPI_(void) ThemeUninitialize() } XmlUninitialize(); + DpiuUninitialize(); } @@ -797,10 +811,10 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( } break; - case WM_WINDOWPOSCHANGED: + case WM_DPICHANGED: + if (OnDpiChanged(pTheme, wParam, lParam)) { - //WINDOWPOS* pos = reinterpret_cast(lParam); - //ThemeWindowPositionChanged(pTheme, pos); + return 0; } break; @@ -1652,6 +1666,7 @@ static HRESULT ParseTheme( ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); pTheme->wId = ++wThemeId; + pTheme->nDpi = USER_DEFAULT_SCREEN_DPI; // Parse the optional background resource image. hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); @@ -1816,6 +1831,7 @@ static HRESULT ParseWindow( { HRESULT hr = S_OK; IXMLDOMNode* pixn = NULL; + DWORD dwValue = 0; BSTR bstr = NULL; LPWSTR sczIconFile = NULL; @@ -1833,7 +1849,7 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window AutoResize attribute."); - hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pTheme->nWidth)); + hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); @@ -1841,7 +1857,9 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window Width attribute."); - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pTheme->nHeight)); + pTheme->nWidth = pTheme->nDefaultDpiWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); @@ -1849,20 +1867,28 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window Height attribute."); - hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", reinterpret_cast(&pTheme->nMinimumWidth)); + pTheme->nHeight = pTheme->nDefaultDpiHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue); if (S_FALSE == hr) { + dwValue = 0; hr = S_OK; } ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); - hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", reinterpret_cast(&pTheme->nMinimumHeight)); + pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue); if (S_FALSE == hr) { + dwValue = 0; hr = S_OK; } ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); + pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue; + hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); if (S_FALSE == hr) { @@ -4161,14 +4187,45 @@ static BOOL OnButtonClicked( LExit: return fHandled; } + +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + UINT nDpi = HIWORD(wParam); + RECT* pRect = reinterpret_cast(lParam); + BOOL fIgnored = pTheme->nDpi == nDpi; + + if (fIgnored) + { + ExitFunction(); + } + + ScaleTheme(pTheme, nDpi, pRect->left, pRect->top); + +LExit: + return !fIgnored; +} static void OnNcCreate( __in THEME* pTheme, __in HWND hWnd, - __in LPARAM /*lParam*/ + __in LPARAM lParam ) { + DPIU_WINDOW_CONTEXT windowContext = { }; + CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); + pTheme->hwndParent = hWnd; + + DpiuGetWindowContext(pTheme->hwndParent, &windowContext); + + if (windowContext.nDpi != pTheme->nDpi) + { + ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y); + } } static HRESULT OnRichEditEnLink( @@ -5282,6 +5339,23 @@ static void ResizeControl( } } +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ) +{ + pTheme->nDpi = nDpi; + + pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi); + pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi); + pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); + pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); + + ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER); +} + static void UnloadControls( __in DWORD cControls, __in THEME_CONTROL* rgControls -- cgit v1.2.3-55-g6feb From 2e0773ed3169889f6246271ef6fffe6f8ce16f89 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 16:07:05 +1000 Subject: Scale the x/y/width/height of theme controls according to the DPI. --- src/dutil/inc/thmutil.h | 5 +++ src/dutil/thmutil.cpp | 107 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 87 insertions(+), 25 deletions(-) diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index 52de755f..e65d0646 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -143,6 +143,10 @@ struct THEME_CONTROL LPWSTR sczText; LPWSTR sczTooltip; LPWSTR sczNote; // optional text for command link + int nDefaultDpiX; + int nDefaultDpiY; + int nDefaultDpiHeight; + int nDefaultDpiWidth; int nX; int nY; int nHeight; @@ -250,6 +254,7 @@ struct THEME WORD wId; BOOL fAutoResize; + BOOL fForceResize; DWORD dwStyle; DWORD dwFontId; diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 4cc149c9..8ff73939 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -313,8 +313,8 @@ static const THEME_CONTROL* FindControlFromHWnd( __in_opt const THEME_CONTROL* pParentControl = NULL ); static void GetControlDimensions( - __in const RECT* prcParent, __in const THEME_CONTROL* pControl, + __in const RECT* prcParent, __out int* piWidth, __out int* piHeight, __out int* piX, @@ -364,6 +364,15 @@ static void ScaleTheme( __in int x, __in int y ); +static void ScaleControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in UINT nDpi + ); +static void ScaleControl( + __in THEME_CONTROL* pControl, + __in UINT nDpi + ); static void GetControls( __in THEME* pTheme, __in_opt THEME_CONTROL* pParentControl, @@ -885,8 +894,9 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( break; case WM_SIZE: - if (pTheme->fAutoResize) + if (pTheme->fAutoResize || pTheme->fForceResize) { + pTheme->fForceResize = FALSE; ::GetClientRect(pTheme->hwndParent, &rcParent); ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent); return 0; @@ -2540,10 +2550,10 @@ static HRESULT ParseControls( if (fBillboardSizing) { - pControl->nX = 0; - pControl->nY = 0; - pControl->nWidth = -0; - pControl->nHeight = 0; + pControl->nX = pControl->nDefaultDpiX = 0; + pControl->nY = pControl->nDefaultDpiY = 0; + pControl->nWidth = pControl->nDefaultDpiWidth = 0; + pControl->nHeight = pControl->nDefaultDpiHeight = 0; } if (pPage) @@ -2616,33 +2626,41 @@ static HRESULT ParseControl( if (!fSkipDimensions) { - hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pControl->nX)); + hr = XmlGetAttributeNumber(pixn, L"X", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } ThmExitOnFailure(hr, "Failed to find control X attribute."); - hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pControl->nY)); + pControl->nX = pControl->nDefaultDpiX = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Y", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } ThmExitOnFailure(hr, "Failed to find control Y attribute."); - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pControl->nHeight)); + pControl->nY = pControl->nDefaultDpiY = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } ThmExitOnFailure(hr, "Failed to find control Height attribute."); - hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pControl->nWidth)); + pControl->nHeight = pControl->nDefaultDpiHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } ThmExitOnFailure(hr, "Failed to find control Width attribute."); + + pControl->nWidth = pControl->nDefaultDpiWidth = dwValue; } // Parse the optional background resource image. @@ -4203,6 +4221,8 @@ static BOOL OnDpiChanged( ExitFunction(); } + + pTheme->fForceResize = !pTheme->fAutoResize; ScaleTheme(pTheme, nDpi, pRect->left, pRect->top); LExit: @@ -4319,18 +4339,18 @@ static const THEME_CONTROL* FindControlFromHWnd( } static void GetControlDimensions( - __in const RECT* prcParent, __in const THEME_CONTROL* pControl, + __in const RECT* prcParent, __out int* piWidth, __out int* piHeight, __out int* piX, __out int* piY ) { - *piWidth = pControl->nWidth < 1 ? pControl->nX < 0 ? prcParent->right + pControl->nWidth : prcParent->right + pControl->nWidth - pControl->nX : pControl->nWidth; - *piHeight = pControl->nHeight < 1 ? pControl->nY < 0 ? prcParent->bottom + pControl->nHeight : prcParent->bottom + pControl->nHeight - pControl->nY : pControl->nHeight; - *piX = pControl->nX < 0 ? prcParent->right + pControl->nX - *piWidth : pControl->nX; - *piY = pControl->nY < 0 ? prcParent->bottom + pControl->nY - *piHeight : pControl->nY; + *piWidth = pControl->nWidth + (0 < pControl->nWidth ? 0 : prcParent->right - max(0, pControl->nX)); + *piHeight = pControl->nHeight + (0 < pControl->nHeight ? 0 : prcParent->bottom - max(0, pControl->nY)); + *piX = pControl->nX + (-1 < pControl->nX ? 0 : prcParent->right - *piWidth); + *piY = pControl->nY + (-1 < pControl->nY ? 0 : prcParent->bottom - *piHeight); } static HRESULT SizeListViewColumns( @@ -4752,6 +4772,10 @@ static HRESULT LoadControls( DWORD cControls = 0; THEME_CONTROL* rgControls = NULL; HWND hwndParent = pParentControl ? pParentControl->hWnd : pTheme->hwndParent; + int w = 0; + int h = 0; + int x = 0; + int y = 0; GetControls(pTheme, pParentControl, cControls, rgControls); ::GetClientRect(hwndParent, &rcParent); @@ -4910,8 +4934,7 @@ static HRESULT LoadControls( pControl->wId = wControlId; - int w, h, x, y; - GetControlDimensions(&rcParent, pControl, &w, &h, &x, &y); + GetControlDimensions(pControl, &rcParent, &w, &h, &x, &y); BOOL fVisible = pControl->dwStyle & WS_VISIBLE; BOOL fDisabled = pControl->dwStyle & WS_DISABLED; @@ -5198,22 +5221,22 @@ static HRESULT LocalizeControl( if (LOC_CONTROL_NOT_SET != pLocControl->nX) { - pControl->nX = pLocControl->nX; + pControl->nDefaultDpiX = pLocControl->nX; } if (LOC_CONTROL_NOT_SET != pLocControl->nY) { - pControl->nY = pLocControl->nY; + pControl->nDefaultDpiY = pLocControl->nY; } if (LOC_CONTROL_NOT_SET != pLocControl->nWidth) { - pControl->nWidth = pLocControl->nWidth; + pControl->nDefaultDpiWidth = pLocControl->nWidth; } if (LOC_CONTROL_NOT_SET != pLocControl->nHeight) { - pControl->nHeight = pLocControl->nHeight; + pControl->nDefaultDpiHeight = pLocControl->nHeight; } if (pLocControl->wzText && *pLocControl->wzText) @@ -5304,10 +5327,14 @@ static void ResizeControl( __in const RECT* prcParent ) { - int w, h, x, y; + int w = 0; + int h = 0; + int x = 0; + int y = 0; + RECT rcControl = { }; - GetControlDimensions(prcParent, pControl, &w, &h, &x, &y); - ::MoveWindow(pControl->hWnd, x, y, w, h, TRUE); + GetControlDimensions(pControl, prcParent, &w, &h, &x, &y); + ::SetWindowPos(pControl->hWnd, NULL, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER); #ifdef DEBUG if (THEME_CONTROL_TYPE_BUTTON == pControl->type) @@ -5333,7 +5360,6 @@ static void ResizeControl( if (pControl->cControls) { - RECT rcControl = { }; ::GetClientRect(pControl->hWnd, &rcControl); ResizeControls(pControl->cControls, pControl->rgControls, &rcControl); } @@ -5353,9 +5379,40 @@ static void ScaleTheme( pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); + ScaleControls(pTheme->cControls, pTheme->rgControls, pTheme->nDpi); + ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER); } +static void ScaleControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in UINT nDpi + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + ScaleControl(pControl, nDpi); + } +} + +static void ScaleControl( + __in THEME_CONTROL* pControl, + __in UINT nDpi + ) +{ + pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi); + pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi); + pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi); + pControl->nY = DpiuScaleValue(pControl->nDefaultDpiY, nDpi); + + if (pControl->cControls) + { + ScaleControls(pControl->cControls, pControl->rgControls, nDpi); + } +} + static void UnloadControls( __in DWORD cControls, __in THEME_CONTROL* rgControls -- cgit v1.2.3-55-g6feb From 07175b9e5d5c89171e34e93b3fb3fb2c11601146 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 16:08:12 +1000 Subject: Update the fonts when using a different DPI. --- src/dutil/inc/thmutil.h | 16 ++++- src/dutil/thmutil.cpp | 155 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 139 insertions(+), 32 deletions(-) diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index e65d0646..41b1e916 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -239,13 +239,27 @@ struct THEME_PAGE THEME_SAVEDVARIABLE* rgSavedVariables; }; -struct THEME_FONT +struct THEME_FONT_INSTANCE { + UINT nDpi; HFONT hFont; +}; + +struct THEME_FONT +{ + LONG lfHeight; + LONG lfWeight; + BYTE lfUnderline; + BYTE lfQuality; + LPWSTR sczFaceName; + COLORREF crForeground; HBRUSH hForeground; COLORREF crBackground; HBRUSH hBackground; + + DWORD cFontInstances; + THEME_FONT_INSTANCE* rgFontInstances; }; diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 8ff73939..1c3ae683 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -36,6 +36,7 @@ const DWORD THEME_INVALID_ID = 0xFFFFFFFF; const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF; +const DWORD GROW_FONT_INSTANCES = 3; const DWORD GROW_WINDOW_TEXT = 250; const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink"; const LPCWSTR THEME_WC_PANEL = L"ThemePanel"; @@ -177,6 +178,11 @@ static HRESULT StartBillboard( __in THEME* pTheme, __in DWORD dwControl ); +static HRESULT EnsureFontInstance( + __in THEME* pTheme, + __in THEME_FONT* pFont, + __out THEME_FONT_INSTANCE** ppFontInstance + ); static HRESULT FindImageList( __in THEME* pTheme, __in_z LPCWSTR wzImageListName, @@ -248,6 +254,9 @@ static DWORD CALLBACK RichEditStreamFromMemoryCallback( __in LONG cb, __in LONG *pcb ); +static void FreeFontInstance( + __in THEME_FONT_INSTANCE* pFontInstance + ); static void FreeFont( __in THEME_FONT* pFont ); @@ -365,11 +374,13 @@ static void ScaleTheme( __in int y ); static void ScaleControls( + __in THEME* pTheme, __in DWORD cControls, __in THEME_CONTROL* rgControls, __in UINT nDpi ); static void ScaleControl( + __in THEME* pTheme, __in THEME_CONTROL* pControl, __in UINT nDpi ); @@ -1078,7 +1089,7 @@ DAPI_(HRESULT) ThemeShowPageEx( else { hr = MemEnsureArraySize(reinterpret_cast(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices); - ThmExitOnNull(pPage->rgSavedVariables, hr, E_OUTOFMEMORY, "Failed to allocate memory for saved variables."); + ThmExitOnFailure(hr, "Failed to allocate memory for saved variables."); SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); pPage->cSavedVariables = pPage->cControlIndices; @@ -2017,7 +2028,6 @@ static HRESULT ParseFonts( IXMLDOMNode* pixn = NULL; BSTR bstrName = NULL; DWORD dwId = 0; - LOGFONTW lf = { }; COLORREF crForeground = THEME_INVISIBLE_COLORREF; COLORREF crBackground = THEME_INVISIBLE_COLORREF; DWORD dwSystemForegroundColor = FALSE; @@ -2037,8 +2047,6 @@ static HRESULT ParseFonts( pTheme->rgFonts = static_cast(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE)); ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); - lf.lfQuality = CLEARTYPE_QUALITY; - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) { hr = XmlGetAttributeNumber(pixn, L"Id", &dwId); @@ -2054,6 +2062,15 @@ static HRESULT ParseFonts( ThmExitOnRootFailure(hr, "Invalid theme font id."); } + THEME_FONT* pFont = pTheme->rgFonts + dwId; + if (pFont->cFontInstances) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Theme font id duplicated."); + } + + pFont->lfQuality = CLEARTYPE_QUALITY; + hr = XmlGetText(pixn, &bstrName); if (S_FALSE == hr) { @@ -2061,28 +2078,28 @@ static HRESULT ParseFonts( } ThmExitOnFailure(hr, "Failed to get font name."); - hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), bstrName); + hr = StrAllocString(&pFont->sczFaceName, bstrName, 0); ThmExitOnFailure(hr, "Failed to copy font name."); - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&lf.lfHeight)); + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pFont->lfHeight)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } ThmExitOnFailure(hr, "Failed to find font height attribute."); - hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&lf.lfWeight)); + hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&pFont->lfWeight)); if (S_FALSE == hr) { - lf.lfWeight = FW_DONTCARE; + pFont->lfWeight = FW_DONTCARE; hr = S_OK; } ThmExitOnFailure(hr, "Failed to find font weight attribute."); - hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&lf.lfUnderline)); + hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&pFont->lfUnderline)); if (E_NOTFOUND == hr) { - lf.lfUnderline = FALSE; + pFont->lfUnderline = FALSE; hr = S_OK; } ThmExitOnFailure(hr, "Failed to find font underline attribute."); @@ -2093,28 +2110,18 @@ static HRESULT ParseFonts( hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor); ThmExitOnFailure(hr, "Failed to find font background color."); - THEME_FONT* pFont = pTheme->rgFonts + dwId; - if (pFont->hFont) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Theme font id duplicated."); - } - - pFont->hFont = ::CreateFontIndirectW(&lf); - ThmExitOnNullWithLastError(pFont->hFont, hr, "Failed to create font %u.", dwId); - pFont->crForeground = crForeground; if (THEME_INVISIBLE_COLORREF != pFont->crForeground) { pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground); - ThmExitOnNullWithLastError(pFont->hForeground, hr, "Failed to create text foreground brush."); + ThmExitOnNull(pFont->hForeground, hr, E_OUTOFMEMORY, "Failed to create text foreground brush."); } pFont->crBackground = crBackground; if (THEME_INVISIBLE_COLORREF != pFont->crBackground) { pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground); - ThmExitOnNullWithLastError(pFont->hBackground, hr, "Failed to create text background brush."); + ThmExitOnNull(pFont->hBackground, hr, E_OUTOFMEMORY, "Failed to create text background brush."); } ReleaseNullBSTR(bstrName); @@ -3577,6 +3584,50 @@ static HRESULT StopBillboard( return hr; } +static HRESULT EnsureFontInstance( + __in THEME* pTheme, + __in THEME_FONT* pFont, + __out THEME_FONT_INSTANCE** ppFontInstance + ) +{ + HRESULT hr = S_OK; + THEME_FONT_INSTANCE* pFontInstance = NULL; + LOGFONTW lf = { }; + + for (DWORD i = 0; i < pFont->cFontInstances; ++i) + { + pFontInstance = pFont->rgFontInstances + i; + if (pTheme->nDpi == pFontInstance->nDpi) + { + *ppFontInstance = pFontInstance; + ExitFunction(); + } + } + + hr = MemEnsureArraySize(reinterpret_cast(&pFont->rgFontInstances), pFont->cFontInstances, sizeof(THEME_FONT_INSTANCE), GROW_FONT_INSTANCES); + ThmExitOnFailure(hr, "Failed to allocate memory for font instances."); + + pFontInstance = pFont->rgFontInstances + pFont->cFontInstances; + pFontInstance->nDpi = pTheme->nDpi; + + lf.lfHeight = DpiuScaleValue(pFont->lfHeight, pFontInstance->nDpi); + lf.lfWeight = pFont->lfWeight; + lf.lfUnderline = pFont->lfUnderline; + lf.lfQuality = pFont->lfQuality; + + hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), pFont->sczFaceName); + ThmExitOnFailure(hr, "Failed to copy font name to create font."); + + pFontInstance->hFont = ::CreateFontIndirectW(&lf); + ThmExitOnNull(pFontInstance->hFont, hr, E_OUTOFMEMORY, "Failed to create DPI specific font."); + + ++pFont->cFontInstances; + *ppFontInstance = pFontInstance; + +LExit: + return hr; +} + static HRESULT FindImageList( __in THEME* pTheme, @@ -3661,9 +3712,11 @@ static void DrawControlText( __in BOOL fDrawFocusRect ) { + HRESULT hr = S_OK; WCHAR wzText[256] = { }; DWORD cchText = 0; THEME_FONT* pFont = NULL; + THEME_FONT_INSTANCE* pFontInstance = NULL; HFONT hfPrev = NULL; if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText)))) @@ -3685,7 +3738,11 @@ static void DrawControlText( pFont = pTheme->rgFonts + pControl->dwFontId; } - hfPrev = SelectFont(pdis->hDC, pFont->hFont); + hr = EnsureFontInstance(pTheme, pFont, &pFontInstance); + if (SUCCEEDED(hr)) + { + hfPrev = SelectFont(pdis->hDC, pFontInstance->hFont); + } ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL); @@ -3694,7 +3751,10 @@ static void DrawControlText( ::DrawFocusRect(pdis->hDC, &pdis->rcItem); } - SelectFont(pdis->hDC, hfPrev); + if (hfPrev) + { + SelectFont(pdis->hDC, hfPrev); + } } @@ -3939,6 +3999,18 @@ static void FreeTab( } +static void FreeFontInstance( + __in THEME_FONT_INSTANCE* pFontInstance + ) +{ + if (pFontInstance->hFont) + { + ::DeleteObject(pFontInstance->hFont); + pFontInstance->hFont = NULL; + } +} + + static void FreeFont( __in THEME_FONT* pFont ) @@ -3957,11 +4029,13 @@ static void FreeFont( pFont->hForeground = NULL; } - if (pFont->hFont) + for (DWORD i = 0; i < pFont->cFontInstances; ++i) { - ::DeleteObject(pFont->hFont); - pFont->hFont = NULL; + FreeFontInstance(&(pFont->rgFontInstances[i])); } + + ReleaseMem(pFont->rgFontInstances); + ReleaseStr(pFont->sczFaceName); } } @@ -4784,6 +4858,7 @@ static HRESULT LoadControls( { THEME_CONTROL* pControl = rgControls + i; THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; + THEME_FONT_INSTANCE* pControlFontInstance = NULL; LPCWSTR wzWindowClass = NULL; DWORD dwWindowBits = WS_CHILD; DWORD dwWindowExBits = 0; @@ -5097,7 +5172,10 @@ static HRESULT LoadControls( if (pControlFont) { - ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFont->hFont, FALSE); + hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); + ThmExitOnFailure(hr, "Failed to get DPI specific font."); + + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFontInstance->hFont, FALSE); } // Initialize the text on all "application" (non-page) controls, best effort only. @@ -5379,12 +5457,13 @@ static void ScaleTheme( pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); - ScaleControls(pTheme->cControls, pTheme->rgControls, pTheme->nDpi); + ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi); ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER); } static void ScaleControls( + __in THEME* pTheme, __in DWORD cControls, __in THEME_CONTROL* rgControls, __in UINT nDpi @@ -5393,15 +5472,29 @@ static void ScaleControls( for (DWORD i = 0; i < cControls; ++i) { THEME_CONTROL* pControl = rgControls + i; - ScaleControl(pControl, nDpi); + ScaleControl(pTheme, pControl, nDpi); } } static void ScaleControl( + __in THEME* pTheme, __in THEME_CONTROL* pControl, __in UINT nDpi ) { + HRESULT hr = S_OK; + THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; + THEME_FONT_INSTANCE* pControlFontInstance = NULL; + + if (pControlFont) + { + hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); + if (SUCCEEDED(hr) && pControl->hWnd) + { + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM)pControlFontInstance->hFont, FALSE); + } + } + pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi); pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi); pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi); @@ -5409,7 +5502,7 @@ static void ScaleControl( if (pControl->cControls) { - ScaleControls(pControl->cControls, pControl->rgControls, nDpi); + ScaleControls(pTheme, pControl->cControls, pControl->rgControls, nDpi); } } -- cgit v1.2.3-55-g6feb From 2f762cfb545e025e550e523986db3d79ba1fb5fa Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 16:09:24 +1000 Subject: Scale the base width of list view columns according to the DPI. --- src/dutil/inc/thmutil.h | 1 + src/dutil/thmutil.cpp | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index 41b1e916..c75f9587 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -86,6 +86,7 @@ struct THEME_COLUMN { LPWSTR pszName; UINT uStringId; + int nDefaultDpiBaseWidth; int nBaseWidth; int nWidth; BOOL fExpands; diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 1c3ae683..b40868a1 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -3137,6 +3137,7 @@ static HRESULT ParseColumns( IXMLDOMNodeList* pixnl = NULL; IXMLDOMNode* pixnChild = NULL; BSTR bstrText = NULL; + DWORD dwValue = 0; hr = XmlSelectNodes(pixn, L"Column", &pixnl); ThmExitOnFailure(hr, "Failed to select child column nodes."); @@ -3152,24 +3153,28 @@ static HRESULT ParseColumns( i = 0; while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) { + THEME_COLUMN* pColumn = pControl->ptcColumns + i; + hr = XmlGetText(pixnChild, &bstrText); ThmExitOnFailure(hr, "Failed to get inner text of column element."); - hr = XmlGetAttributeNumber(pixnChild, L"Width", reinterpret_cast(&pControl->ptcColumns[i].nBaseWidth)); + hr = XmlGetAttributeNumber(pixnChild, L"Width", &dwValue); if (S_FALSE == hr) { - pControl->ptcColumns[i].nBaseWidth = 100; + dwValue = 100; } ThmExitOnFailure(hr, "Failed to get column width attribute."); - hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pControl->ptcColumns[i].fExpands)); + pColumn->nBaseWidth = pColumn->nDefaultDpiBaseWidth = dwValue; + + hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pColumn->fExpands)); if (E_NOTFOUND == hr) { hr = S_OK; } ThmExitOnFailure(hr, "Failed to get expands attribute."); - hr = StrAllocString(&(pControl->ptcColumns[i].pszName), bstrText, 0); + hr = StrAllocString(&pColumn->pszName, bstrText, 0); ThmExitOnFailure(hr, "Failed to copy column name."); ++i; @@ -5495,6 +5500,16 @@ static void ScaleControl( } } + if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + THEME_COLUMN* pColumn = pControl->ptcColumns + i; + + pColumn->nBaseWidth = DpiuScaleValue(pColumn->nDefaultDpiBaseWidth, nDpi); + } + } + pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi); pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi); pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi); -- cgit v1.2.3-55-g6feb From 00bd06f02cf168eb0752e1d0819969a528742ba7 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 16:10:13 +1000 Subject: Use the DefaultDPI width and height as the source width and height for images. --- src/dutil/thmutil.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index b40868a1..4da489e4 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -1258,8 +1258,10 @@ DAPI_(HRESULT) ThemeDrawBackground( { HDC hdcMem = ::CreateCompatibleDC(pps->hdc); HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pTheme->hImage)); + DWORD dwSourceWidth = pTheme->nDefaultDpiWidth; + DWORD dwSourceHeight = pTheme->nDefaultDpiHeight; - ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, pTheme->nWidth, pTheme->nHeight, SRCCOPY); + ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); ::SelectObject(hdcMem, hDefaultBitmap); ::DeleteDC(hdcMem); @@ -3666,6 +3668,8 @@ static HRESULT DrawButton( { int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + DWORD dwSourceWidth = pControl->nDefaultDpiWidth; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); @@ -3674,20 +3678,20 @@ static HRESULT DrawButton( // "clicked" gets priority if (ODS_SELECTED & pdis->itemState) { - nSourceY += pControl->nHeight * 2; + nSourceY += pControl->nDefaultDpiHeight * 2; } // then hover else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) { - nSourceY += pControl->nHeight; + nSourceY += pControl->nDefaultDpiHeight; } // then focused else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState) { - nSourceY += pControl->nHeight * 3; + nSourceY += pControl->nDefaultDpiHeight * 3; } - ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, pControl->nWidth, pControl->nHeight, SRCCOPY); + ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); ::SelectObject(hdcMem, hDefaultBitmap); ::DeleteDC(hdcMem); @@ -3773,6 +3777,8 @@ static HRESULT DrawImage( DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left; int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; + DWORD dwSourceWidth = pControl->nDefaultDpiWidth; BLENDFUNCTION bf = { }; bf.BlendOp = AC_SRC_OVER; @@ -3784,9 +3790,9 @@ static HRESULT DrawImage( // Try to draw the image with transparency and if that fails (usually because the image has no // alpha channel) then draw the image as is. - if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwWidth, dwHeight, bf)) + if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, bf)) { - ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwWidth, dwHeight, SRCCOPY); + ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); } ::SelectObject(hdcMem, hDefaultBitmap); @@ -3805,29 +3811,30 @@ static HRESULT DrawProgressBar( DWORD dwProgressPercentage = LOWORD(pControl->dwData); DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; - int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * pControl->nHeight); + int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * dwSourceHeight); HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); // Draw the left side of the progress bar. - ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwHeight, SRCCOPY); + ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY); // Draw the filled side of the progress bar, if there is any. if (0 < dwCenter) { - ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwHeight, SRCCOPY); + ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwSourceHeight, SRCCOPY); } // Draw the unfilled side of the progress bar, if there is any. if (dwCenter < static_cast(pdis->rcItem.right - 2)) { - ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwHeight, SRCCOPY); + ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwSourceHeight, SRCCOPY); } // Draw the right side of the progress bar. - ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwHeight, SRCCOPY); + ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY); ::SelectObject(hdcMem, hDefaultBitmap); ::DeleteDC(hdcMem); -- cgit v1.2.3-55-g6feb From abeba64d77336b3fbf9aafe9ecc66b779c1e5d02 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 16:22:38 +1000 Subject: Add ability for ThemeCreateParentWindow to center on the monitor. --- src/dutil/dpiutil.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ src/dutil/inc/dpiutil.h | 15 ++++++++++++++ src/dutil/inc/thmutil.h | 7 +++++++ src/dutil/thmutil.cpp | 34 ++++++++++++++++++++++++++++++- 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/src/dutil/dpiutil.cpp b/src/dutil/dpiutil.cpp index edab8f01..a27ee8c5 100644 --- a/src/dutil/dpiutil.cpp +++ b/src/dutil/dpiutil.cpp @@ -62,6 +62,59 @@ DAPI_(void) DpiuUninitialize() vfDpiuInitialized = FALSE; } +DAPI_(HRESULT) DpiuGetMonitorContextFromPoint( + __in const POINT* pt, + __out DPIU_MONITOR_CONTEXT** ppMonitorContext + ) +{ + HRESULT hr = S_OK; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + HMONITOR hMonitor = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + HDC hdc = NULL; + + pMonitorContext = reinterpret_cast(MemAlloc(sizeof(DPIU_MONITOR_CONTEXT), TRUE)); + DpiuExitOnNull(pMonitorContext, hr, E_OUTOFMEMORY, "Failed to allocate memory for DpiuMonitorContext."); + + hMonitor = ::MonitorFromPoint(*pt, MONITOR_DEFAULTTONEAREST); + DpiuExitOnNull(hMonitor, hr, E_FAIL, "Failed to get monitor from point."); + + pMonitorContext->mi.cbSize = sizeof(pMonitorContext->mi); + if (!::GetMonitorInfoW(hMonitor, &pMonitorContext->mi)) + { + DpiuExitOnFailure(hr = E_OUTOFMEMORY, "Failed to get monitor info for point."); + } + + if (vpfnGetDpiForMonitor) + { + hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + DpiuExitOnFailure(hr, "Failed to get DPI for monitor."); + + pMonitorContext->nDpi = dpiX; + } + else + { + hdc = ::CreateDCW(L"DISPLAY", pMonitorContext->mi.szDevice, NULL, NULL); + DpiuExitOnNull(hdc, hr, E_OUTOFMEMORY, "Failed to get device context for monitor."); + + pMonitorContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); + } + + *ppMonitorContext = pMonitorContext; + pMonitorContext = NULL; + +LExit: + if (hdc) + { + ::ReleaseDC(NULL, hdc); + } + + MemFree(pMonitorContext); + + return hr; +} + DAPI_(void) DpiuGetWindowContext( __in HWND hWnd, __in DPIU_WINDOW_CONTEXT* pWindowContext diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h index c6f73b02..8c61ab19 100644 --- a/src/dutil/inc/dpiutil.h +++ b/src/dutil/inc/dpiutil.h @@ -14,6 +14,12 @@ extern "C" { #define USER_DEFAULT_SCREEN_DPI 96 #endif +typedef struct _DPIU_MONITOR_CONTEXT +{ + UINT nDpi; + MONITORINFOEXW mi; +} DPIU_MONITOR_CONTEXT; + typedef struct _DPIU_WINDOW_CONTEXT { UINT nDpi; @@ -32,6 +38,15 @@ typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( void DAPI DpiuInitialize(); void DAPI DpiuUninitialize(); +/******************************************************************** + DpiuGetMonitorContextFromPoint - get the DPI context of the monitor from the given point. + +*******************************************************************/ +HRESULT DAPI DpiuGetMonitorContextFromPoint( + __in const POINT* pt, + __out DPIU_MONITOR_CONTEXT** ppMonitorContext + ); + /******************************************************************** DpiuGetWindowContext - get the DPI context of the given window. diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index c75f9587..1ba5db35 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -81,6 +81,12 @@ typedef enum THEME_SHOW_PAGE_REASON THEME_SHOW_PAGE_REASON_REFRESH, } THEME_SHOW_PAGE_REASON; +typedef enum THEME_WINDOW_INITIAL_POSITION +{ + THEME_WINDOW_INITIAL_POSITION_DEFAULT, + THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, +} THEME_WINDOW_INITIAL_POSITION; + struct THEME_COLUMN { @@ -394,6 +400,7 @@ HRESULT DAPI ThemeCreateParentWindow( __in_opt HWND hwndParent, __in_opt HINSTANCE hInstance, __in_opt LPVOID lpParam, + __in THEME_WINDOW_INITIAL_POSITION initialPosition, __out_opt HWND* phWnd ); diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 4da489e4..a1b101e8 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -603,10 +603,14 @@ DAPI_(HRESULT) ThemeCreateParentWindow( __in_opt HWND hwndParent, __in_opt HINSTANCE hInstance, __in_opt LPVOID lpParam, + __in THEME_WINDOW_INITIAL_POSITION initialPosition, __out_opt HWND* phWnd ) { HRESULT hr = S_OK; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + POINT pt = { }; + RECT* pMonitorRect = NULL; HWND hWnd = NULL; if (pTheme->hwndParent) @@ -614,6 +618,29 @@ DAPI_(HRESULT) ThemeCreateParentWindow( ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded."); } + if (THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES == initialPosition) + { + pt.x = x; + pt.y = y; + hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); + if (SUCCEEDED(hr)) + { + pMonitorRect = &pMonitorContext->mi.rcWork; + if (pMonitorContext->nDpi != pTheme->nDpi) + { + ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top); + } + + x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWidth) / 2; + y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nHeight) / 2; + } + else + { + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + } + } + hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWidth, pTheme->nHeight, hwndParent, NULL, hInstance, lpParam); ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window."); ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE."); @@ -625,6 +652,8 @@ DAPI_(HRESULT) ThemeCreateParentWindow( } LExit: + MemFree(pMonitorContext); + return hr; } @@ -5471,7 +5500,10 @@ static void ScaleTheme( ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi); - ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER); + if (pTheme->hwndParent) + { + ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER); + } } static void ScaleControls( -- cgit v1.2.3-55-g6feb From 219964095a1f4678d8c8e7ae2685c52392161ca2 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 19:22:37 +1000 Subject: WIXFEAT:4906 Make Window's Height and Width refer to its client area. --- src/dutil/dpiutil.cpp | 26 +++++++++++++++++++++ src/dutil/inc/dpiutil.h | 21 +++++++++++++++++ src/dutil/inc/thmutil.h | 2 ++ src/dutil/thmutil.cpp | 59 ++++++++++++++++++++++++++++++++++++++--------- src/dutil/xsd/thmutil.xsd | 4 ++-- 5 files changed, 99 insertions(+), 13 deletions(-) diff --git a/src/dutil/dpiutil.cpp b/src/dutil/dpiutil.cpp index a27ee8c5..02a9580c 100644 --- a/src/dutil/dpiutil.cpp +++ b/src/dutil/dpiutil.cpp @@ -15,6 +15,7 @@ #define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) #define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__) +static PFN_ADJUSTWINDOWRECTEXFORDPI vpfnAdjustWindowRectExForDpi = NULL; static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; @@ -37,6 +38,7 @@ DAPI_(void) DpiuInitialize() if (SUCCEEDED(hr)) { // Ignore failures. + vpfnAdjustWindowRectExForDpi = reinterpret_cast(::GetProcAddress(vhUser32Dll, "AdjustWindowRectExForDpi")); vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); } @@ -57,11 +59,35 @@ DAPI_(void) DpiuUninitialize() vhShcoreDll = NULL; vhUser32Dll = NULL; + vpfnAdjustWindowRectExForDpi = NULL; vpfnGetDpiForMonitor = NULL; vpfnGetDpiForWindow = NULL; vfDpiuInitialized = FALSE; } +DAPI_(void) DpiuAdjustWindowRect( + __in RECT* pWindowRect, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle, + __in UINT nDpi + ) +{ + if (WS_SYSMENU & dwStyle) + { + dwStyle |= WS_CAPTION; // WS_CAPTION is required with WS_SYSMENU, AdjustWindowRect* won't work properly when it's not specified. + } + + if (vpfnAdjustWindowRectExForDpi) + { + vpfnAdjustWindowRectExForDpi(pWindowRect, dwStyle, fMenu, dwExStyle, nDpi); + } + else + { + ::AdjustWindowRectEx(pWindowRect, dwStyle, fMenu, dwExStyle); + } +} + DAPI_(HRESULT) DpiuGetMonitorContextFromPoint( __in const POINT* pt, __out DPIU_MONITOR_CONTEXT** ppMonitorContext diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h index 8c61ab19..4ea689c5 100644 --- a/src/dutil/inc/dpiutil.h +++ b/src/dutil/inc/dpiutil.h @@ -25,6 +25,13 @@ typedef struct _DPIU_WINDOW_CONTEXT UINT nDpi; } DPIU_WINDOW_CONTEXT; +typedef BOOL (APIENTRY* PFN_ADJUSTWINDOWRECTEXFORDPI)( + __in LPRECT lpRect, + __in DWORD dwStyle, + __in BOOL bMenu, + __in DWORD dwExStyle, + __in UINT dpi + ); typedef HRESULT (APIENTRY *PFN_GETDPIFORMONITOR)( __in HMONITOR hmonitor, __in MONITOR_DPI_TYPE dpiType, @@ -38,6 +45,20 @@ typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( void DAPI DpiuInitialize(); void DAPI DpiuUninitialize(); +/******************************************************************** + DpiuAdjustWindowRect - calculate the required size of the window rectangle, + based on the desired size of the client rectangle + and the provided DPI. + +*******************************************************************/ +void DAPI DpiuAdjustWindowRect( + __in RECT* pWindowRect, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle, + __in UINT nDpi + ); + /******************************************************************** DpiuGetMonitorContextFromPoint - get the DPI context of the monitor from the given point. diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index 1ba5db35..00fa1381 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -289,6 +289,8 @@ struct THEME int nMinimumHeight; int nWidth; int nMinimumWidth; + int nWindowHeight; + int nWindowWidth; int nSourceX; int nSourceY; UINT uStringId; diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index a1b101e8..75b0e4a0 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -367,12 +367,21 @@ static void ResizeControl( __in THEME_CONTROL* pControl, __in const RECT* prcParent ); -static void ScaleTheme( +static void ScaleThemeFromWindow( __in THEME* pTheme, __in UINT nDpi, __in int x, __in int y ); +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle + ); static void ScaleControls( __in THEME* pTheme, __in DWORD cControls, @@ -611,6 +620,7 @@ DAPI_(HRESULT) ThemeCreateParentWindow( DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; POINT pt = { }; RECT* pMonitorRect = NULL; + HMENU hMenu = NULL; HWND hWnd = NULL; if (pTheme->hwndParent) @@ -628,11 +638,11 @@ DAPI_(HRESULT) ThemeCreateParentWindow( pMonitorRect = &pMonitorContext->mi.rcWork; if (pMonitorContext->nDpi != pTheme->nDpi) { - ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top); + ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top, dwStyle, NULL != hMenu, dwExStyle); } - x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWidth) / 2; - y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nHeight) / 2; + x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWindowWidth) / 2; + y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nWindowHeight) / 2; } else { @@ -641,7 +651,7 @@ DAPI_(HRESULT) ThemeCreateParentWindow( } } - hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWidth, pTheme->nHeight, hwndParent, NULL, hInstance, lpParam); + hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, hwndParent, hMenu, hInstance, lpParam); ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window."); ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE."); AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window."); @@ -1909,7 +1919,7 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window Width attribute."); - pTheme->nWidth = pTheme->nDefaultDpiWidth = dwValue; + pTheme->nWidth = pTheme->nDefaultDpiWidth = pTheme->nWindowWidth = dwValue; hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); if (S_FALSE == hr) @@ -1919,7 +1929,7 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window Height attribute."); - pTheme->nHeight = pTheme->nDefaultDpiHeight = dwValue; + pTheme->nHeight = pTheme->nDefaultDpiHeight = pTheme->nWindowHeight = dwValue; hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue); if (S_FALSE == hr) @@ -4338,7 +4348,7 @@ static BOOL OnDpiChanged( pTheme->fForceResize = !pTheme->fAutoResize; - ScaleTheme(pTheme, nDpi, pRect->left, pRect->top); + ScaleThemeFromWindow(pTheme, nDpi, pRect->left, pRect->top); LExit: return !fIgnored; @@ -4359,7 +4369,7 @@ static void OnNcCreate( if (windowContext.nDpi != pTheme->nDpi) { - ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y); + ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y, pCreateStruct->style, NULL != pCreateStruct->hMenu, pCreateStruct->dwExStyle); } } @@ -5484,13 +5494,32 @@ static void ResizeControl( } } -static void ScaleTheme( +static void ScaleThemeFromWindow( __in THEME* pTheme, __in UINT nDpi, __in int x, __in int y ) { + DWORD dwStyle = GetWindowStyle(pTheme->hwndParent); + BOOL fMenu = NULL != ::GetMenu(pTheme->hwndParent); + DWORD dwExStyle = GetWindowExStyle(pTheme->hwndParent); + + ScaleTheme(pTheme, nDpi, x, y, dwStyle, fMenu, dwExStyle); +} + +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle + ) +{ + RECT rect = { }; + pTheme->nDpi = nDpi; pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi); @@ -5498,11 +5527,19 @@ static void ScaleTheme( pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); + rect.left = x; + rect.top = y; + rect.right = x + pTheme->nWidth; + rect.bottom = y + pTheme->nHeight; + DpiuAdjustWindowRect(&rect, dwStyle, fMenu, dwExStyle, pTheme->nDpi); + pTheme->nWindowWidth = rect.right - rect.left; + pTheme->nWindowHeight = rect.bottom - rect.top; + ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi); if (pTheme->hwndParent) { - ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER); + ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, SWP_NOACTIVATE | SWP_NOZORDER); } } diff --git a/src/dutil/xsd/thmutil.xsd b/src/dutil/xsd/thmutil.xsd index ccf951c0..46c20e4a 100644 --- a/src/dutil/xsd/thmutil.xsd +++ b/src/dutil/xsd/thmutil.xsd @@ -165,7 +165,7 @@ - Height of the window. + Height of the window's client area. @@ -219,7 +219,7 @@ - Width of the window. + Width of the window's client area. -- cgit v1.2.3-55-g6feb From 171b886bc5652da63b5ed549c755aa3fa3337eab Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 19:43:10 +1000 Subject: Split up ThemeDefWindowProc so that the right message handling code is shared with PanelWndProc. --- src/dutil/thmutil.cpp | 235 ++++++++++++++++++++++++++++---------------------- 1 file changed, 132 insertions(+), 103 deletions(-) diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 75b0e4a0..6025749e 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -334,6 +334,13 @@ static void GetControlDimensions( static HRESULT SizeListViewColumns( __inout THEME_CONTROL* pControl ); +static LRESULT CALLBACK ControlGroupDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); static LRESULT CALLBACK PanelWndProc( __in HWND hWnd, __in UINT uMsg, @@ -860,7 +867,14 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( switch (uMsg) { case WM_NCCREATE: - OnNcCreate(pTheme, hWnd, lParam); + if (pTheme->hwndParent) + { + AssertSz(FALSE, "WM_NCCREATE called multiple times"); + } + else + { + OnNcCreate(pTheme, hWnd, lParam); + } break; case WM_NCHITTEST: @@ -877,41 +891,6 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( } break; - case WM_DRAWITEM: - ThemeDrawControl(pTheme, reinterpret_cast(lParam)); - return TRUE; - - case WM_CTLCOLORBTN: __fallthrough; - case WM_CTLCOLORSTATIC: - { - HBRUSH hBrush = NULL; - if (ThemeSetControlColor(pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam), &hBrush)) - { - return reinterpret_cast(hBrush); - } - } - break; - - case WM_SETCURSOR: - if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast(wParam))) - { - return TRUE; - } - break; - - case WM_PAINT: - if (::GetUpdateRect(hWnd, NULL, FALSE)) - { - PAINTSTRUCT ps; - ::BeginPaint(hWnd, &ps); - if (hWnd == pTheme->hwndParent) - { - ThemeDrawBackground(pTheme, &ps); - } - ::EndPaint(hWnd, &ps); - } - return 0; - case WM_SIZING: if (pTheme->fAutoResize) { @@ -952,70 +931,10 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( return 0; } break; - - case WM_TIMER: - OnBillboardTimer(pTheme, hWnd, wParam); - break; - - case WM_NOTIFY: - if (lParam) - { - LPNMHDR pnmhdr = reinterpret_cast(lParam); - switch (pnmhdr->code) - { - // Tab/Shift+Tab support for rich-edit control. - case EN_MSGFILTER: - { - MSGFILTER* msgFilter = reinterpret_cast(lParam); - if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam) - { - BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT); - HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift); - ::SetFocus(hwndFocus); - return 1; - } - break; - } - - // Hyperlink clicks from rich-edit control. - case EN_LINK: - return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd)); - - // Clicks on a hypertext/syslink control. - case NM_CLICK: __fallthrough; - case NM_RETURN: - if (ControlIsType(pTheme, static_cast(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT)) - { - PNMLINK pnmlink = reinterpret_cast(lParam); - LITEM litem = pnmlink->item; - ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); - return 1; - } - - return 0; - } - } - break; - - case WM_COMMAND: - switch (HIWORD(wParam)) - { - case BN_CLICKED: - if (lParam) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam); - if (pControl && OnButtonClicked(pTheme, hWnd, pControl)) - { - return 0; - } - } - break; - } - break; } } - return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); } @@ -4846,6 +4765,119 @@ LExit: } +static LRESULT CALLBACK ControlGroupDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + if (pTheme) + { + switch (uMsg) + { + case WM_DRAWITEM: + ThemeDrawControl(pTheme, reinterpret_cast(lParam)); + return TRUE; + + case WM_CTLCOLORBTN: __fallthrough; + case WM_CTLCOLORSTATIC: + { + HBRUSH hBrush = NULL; + if (ThemeSetControlColor(pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam), &hBrush)) + { + return reinterpret_cast(hBrush); + } + } + break; + + case WM_SETCURSOR: + if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast(wParam))) + { + return TRUE; + } + break; + + case WM_PAINT: + if (::GetUpdateRect(hWnd, NULL, FALSE)) + { + PAINTSTRUCT ps; + ::BeginPaint(hWnd, &ps); + if (hWnd == pTheme->hwndParent) + { + ThemeDrawBackground(pTheme, &ps); + } + ::EndPaint(hWnd, &ps); + } + return 0; + + case WM_TIMER: + OnBillboardTimer(pTheme, hWnd, wParam); + break; + + case WM_NOTIFY: + if (lParam) + { + LPNMHDR pnmhdr = reinterpret_cast(lParam); + switch (pnmhdr->code) + { + // Tab/Shift+Tab support for rich-edit control. + case EN_MSGFILTER: + { + MSGFILTER* msgFilter = reinterpret_cast(lParam); + if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam) + { + BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT); + HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift); + ::SetFocus(hwndFocus); + return 1; + } + break; + } + + // Hyperlink clicks from rich-edit control. + case EN_LINK: + return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd)); + + // Clicks on a hypertext/syslink control. + case NM_CLICK: __fallthrough; + case NM_RETURN: + if (ControlIsType(pTheme, static_cast(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT)) + { + PNMLINK pnmlink = reinterpret_cast(lParam); + LITEM litem = pnmlink->item; + ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); + return 1; + } + + return 0; + } + } + break; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + if (lParam) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam); + if (pControl && OnButtonClicked(pTheme, hWnd, pControl)) + { + return 0; + } + } + break; + } + break; + } + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + + static LRESULT CALLBACK PanelWndProc( __in HWND hWnd, __in UINT uMsg, @@ -4861,7 +4893,8 @@ static LRESULT CALLBACK PanelWndProc( case WM_NCCREATE: { LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); + pTheme = reinterpret_cast(lpcs->lpCreateParams); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pTheme)); } break; @@ -4873,13 +4906,9 @@ static LRESULT CALLBACK PanelWndProc( case WM_NCHITTEST: return HTCLIENT; break; - - case WM_DRAWITEM: - ThemeDrawControl(pTheme, reinterpret_cast(lParam)); - return TRUE; } - return ThemeDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); + return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); } -- cgit v1.2.3-55-g6feb From f651268309263fa67fa84caf9fb6dbebb5c900d9 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 7 Jul 2020 14:04:39 +1000 Subject: Add DpiuSetProcessDpiAwareness. --- src/dutil/dpiutil.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++++++++ src/dutil/inc/dpiutil.h | 26 +++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/dutil/dpiutil.cpp b/src/dutil/dpiutil.cpp index 02a9580c..4096c8d3 100644 --- a/src/dutil/dpiutil.cpp +++ b/src/dutil/dpiutil.cpp @@ -18,6 +18,9 @@ static PFN_ADJUSTWINDOWRECTEXFORDPI vpfnAdjustWindowRectExForDpi = NULL; static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; +static PFN_SETPROCESSDPIAWARE vpfnSetProcessDPIAware = NULL; +static PFN_SETPROCESSDPIAWARENESS vpfnSetProcessDpiAwareness = NULL; +static PFN_SETPROCESSDPIAWARENESSCONTEXT vpfnSetProcessDpiAwarenessContext = NULL; static HMODULE vhShcoreDll = NULL; static HMODULE vhUser32Dll = NULL; @@ -32,6 +35,7 @@ DAPI_(void) DpiuInitialize() { // Ignore failures. vpfnGetDpiForMonitor = reinterpret_cast(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor")); + vpfnSetProcessDpiAwareness = reinterpret_cast(::GetProcAddress(vhShcoreDll, "SetProcessDpiAwareness")); } hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll); @@ -40,6 +44,8 @@ DAPI_(void) DpiuInitialize() // Ignore failures. vpfnAdjustWindowRectExForDpi = reinterpret_cast(::GetProcAddress(vhUser32Dll, "AdjustWindowRectExForDpi")); vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); + vpfnSetProcessDPIAware = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDPIAware")); + vpfnSetProcessDpiAwarenessContext = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDpiAwarenessContext")); } vfDpiuInitialized = TRUE; @@ -194,3 +200,75 @@ DAPI_(int) DpiuScaleValue( { return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI); } + +DAPI_(HRESULT) DpiuSetProcessDpiAwareness( + __in DPIU_AWARENESS supportedAwareness, + __in_opt DPIU_AWARENESS* pSelectedAwareness + ) +{ + HRESULT hr = S_OK; + DPIU_AWARENESS selectedAwareness = DPIU_AWARENESS_NONE; + DPI_AWARENESS_CONTEXT awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE; + PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE; + + if (vpfnSetProcessDpiAwarenessContext) + { + if (DPIU_AWARENESS_PERMONITORV2 & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + selectedAwareness = DPIU_AWARENESS_PERMONITORV2; + } + else if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; + selectedAwareness = DPIU_AWARENESS_PERMONITOR; + } + else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + selectedAwareness = DPIU_AWARENESS_SYSTEM; + } + else if (DPIU_AWARENESS_GDISCALED & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED; + selectedAwareness = DPIU_AWARENESS_GDISCALED; + } + + if (!vpfnSetProcessDpiAwarenessContext(awarenessContext)) + { + DpiuExitOnLastError(hr, "Failed to set process DPI awareness context."); + } + } + else if (vpfnSetProcessDpiAwareness) + { + if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) + { + awareness = PROCESS_PER_MONITOR_DPI_AWARE; + selectedAwareness = DPIU_AWARENESS_PERMONITOR; + } + else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) + { + awareness = PROCESS_SYSTEM_DPI_AWARE; + selectedAwareness = DPIU_AWARENESS_SYSTEM; + } + + hr = vpfnSetProcessDpiAwareness(awareness); + DpiuExitOnFailure(hr, "Failed to set process DPI awareness."); + } + else if (vpfnSetProcessDPIAware && (DPIU_AWARENESS_SYSTEM & supportedAwareness)) + { + selectedAwareness = DPIU_AWARENESS_SYSTEM; + if (!vpfnSetProcessDPIAware()) + { + DpiuExitOnLastError(hr, "Failed to set process DPI aware."); + } + } + +LExit: + if (pSelectedAwareness) + { + *pSelectedAwareness = selectedAwareness; + } + + return hr; +} diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h index 4ea689c5..216d3dc6 100644 --- a/src/dutil/inc/dpiutil.h +++ b/src/dutil/inc/dpiutil.h @@ -14,6 +14,15 @@ extern "C" { #define USER_DEFAULT_SCREEN_DPI 96 #endif +typedef enum DPIU_AWARENESS +{ + DPIU_AWARENESS_NONE = 0x0, + DPIU_AWARENESS_SYSTEM = 0x1, + DPIU_AWARENESS_PERMONITOR = 0x2, + DPIU_AWARENESS_PERMONITORV2 = 0x4, + DPIU_AWARENESS_GDISCALED = 0x8, +} DPIU_PROCESS_AWARENESS; + typedef struct _DPIU_MONITOR_CONTEXT { UINT nDpi; @@ -41,6 +50,13 @@ typedef HRESULT (APIENTRY *PFN_GETDPIFORMONITOR)( typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( __in HWND hwnd ); +typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARE)(); +typedef HRESULT (APIENTRY* PFN_SETPROCESSDPIAWARENESS)( + __in PROCESS_DPI_AWARENESS value + ); +typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARENESSCONTEXT)( + __in DPI_AWARENESS_CONTEXT value + ); void DAPI DpiuInitialize(); void DAPI DpiuUninitialize(); @@ -86,6 +102,16 @@ int DAPI DpiuScaleValue( __in UINT nTargetDpi ); +/******************************************************************** + DpiuSetProcessDpiAwareness - set the process DPI awareness. The ranking is + PERMONITORV2 > PERMONITOR > SYSTEM > GDISCALED > NONE. + +*******************************************************************/ +HRESULT DAPI DpiuSetProcessDpiAwareness( + __in DPIU_AWARENESS supportedAwareness, + __in_opt DPIU_AWARENESS* pSelectedAwareness + ); + #ifdef __cplusplus } #endif -- cgit v1.2.3-55-g6feb From 9c4b5559ccb55491fe68b0096d1be0496fd6fcc7 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 11 Jul 2020 21:11:30 +1000 Subject: Import DUtilUnitTest from old wix4 repo. --- src/test/DUtilUnitTest/AssemblyInfo.cpp | 12 + src/test/DUtilUnitTest/CondUtilTest.cpp | 190 ++++++++ src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 73 +++ .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 77 +++ src/test/DUtilUnitTest/DictUtilTest.cpp | 187 ++++++++ src/test/DUtilUnitTest/DirUtilTests.cpp | 70 +++ src/test/DUtilUnitTest/FileUtilTest.cpp | 118 +++++ src/test/DUtilUnitTest/GuidUtilTest.cpp | 60 +++ src/test/DUtilUnitTest/IniUtilTest.cpp | 342 +++++++++++++ src/test/DUtilUnitTest/MemUtilTest.cpp | 491 +++++++++++++++++++ src/test/DUtilUnitTest/MonUtilTest.cpp | 487 +++++++++++++++++++ src/test/DUtilUnitTest/NativeAssert.h | 83 ++++ src/test/DUtilUnitTest/PathUtilTest.cpp | 80 ++++ src/test/DUtilUnitTest/SceUtilTest.cpp | 488 +++++++++++++++++++ src/test/DUtilUnitTest/StrUtilTest.cpp | 184 +++++++ src/test/DUtilUnitTest/UnitTest.rc | 7 + src/test/DUtilUnitTest/UriUtilTest.cpp | 96 ++++ src/test/DUtilUnitTest/VarHelpers.cpp | 147 ++++++ src/test/DUtilUnitTest/VarHelpers.h | 20 + src/test/DUtilUnitTest/VarUtilTest.cpp | 532 +++++++++++++++++++++ src/test/DUtilUnitTest/error.h | 8 + src/test/DUtilUnitTest/packages.config | 7 + src/test/DUtilUnitTest/precomp.cpp | 3 + src/test/DUtilUnitTest/precomp.h | 30 ++ src/test/DUtilUnitTest/resource.h | 2 + 25 files changed, 3794 insertions(+) create mode 100644 src/test/DUtilUnitTest/AssemblyInfo.cpp create mode 100644 src/test/DUtilUnitTest/CondUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/DUtilUnitTest.vcxproj create mode 100644 src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters create mode 100644 src/test/DUtilUnitTest/DictUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/DirUtilTests.cpp create mode 100644 src/test/DUtilUnitTest/FileUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/GuidUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/IniUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/MemUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/MonUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/NativeAssert.h create mode 100644 src/test/DUtilUnitTest/PathUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/SceUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/StrUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/UnitTest.rc create mode 100644 src/test/DUtilUnitTest/UriUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/VarHelpers.cpp create mode 100644 src/test/DUtilUnitTest/VarHelpers.h create mode 100644 src/test/DUtilUnitTest/VarUtilTest.cpp create mode 100644 src/test/DUtilUnitTest/error.h create mode 100644 src/test/DUtilUnitTest/packages.config create mode 100644 src/test/DUtilUnitTest/precomp.cpp create mode 100644 src/test/DUtilUnitTest/precomp.h create mode 100644 src/test/DUtilUnitTest/resource.h diff --git a/src/test/DUtilUnitTest/AssemblyInfo.cpp b/src/test/DUtilUnitTest/AssemblyInfo.cpp new file mode 100644 index 00000000..2d527910 --- /dev/null +++ b/src/test/DUtilUnitTest/AssemblyInfo.cpp @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; + +[assembly: AssemblyTitleAttribute("Windows Installer XML Dutil unit tests")]; +[assembly: AssemblyDescriptionAttribute("Dutil unit tests")]; +[assembly: AssemblyCultureAttribute("")]; +[assembly: ComVisible(false)]; diff --git a/src/test/DUtilUnitTest/CondUtilTest.cpp b/src/test/DUtilUnitTest/CondUtilTest.cpp new file mode 100644 index 00000000..c808363d --- /dev/null +++ b/src/test/DUtilUnitTest/CondUtilTest.cpp @@ -0,0 +1,190 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace DutilTests +{ + using namespace System; + using namespace Xunit; + using namespace WixTest; + + public ref class CondUtil + { + public: + [NamedFact(Skip = "condutil Not Implemented Yet.")] + void CondEvaluateTest() + { + HRESULT hr = S_OK; + VARIABLES_HANDLE pVariables = NULL; + + try + { + hr = VarCreate(&pVariables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + // set variables + VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); + VarSetStringHelper(pVariables, L"PROP2", L"VAL2"); + VarSetStringHelper(pVariables, L"PROP3", L"VAL3"); + VarSetStringHelper(pVariables, L"PROP4", L"BEGIN MID END"); + VarSetNumericHelper(pVariables, L"PROP5", 5); + VarSetNumericHelper(pVariables, L"PROP6", 6); + VarSetStringHelper(pVariables, L"PROP7", L""); + VarSetNumericHelper(pVariables, L"PROP8", 0); + VarSetStringHelper(pVariables, L"_PROP9", L"VAL9"); + VarSetNumericHelper(pVariables, L"PROP10", -10); + VarSetNumericHelper(pVariables, L"PROP11", 9223372036854775807ll); + VarSetNumericHelper(pVariables, L"PROP12", -9223372036854775808ll); + VarSetNumericHelper(pVariables, L"PROP13", 0x00010000); + VarSetNumericHelper(pVariables, L"PROP14", 0x00000001); + VarSetNumericHelper(pVariables, L"PROP15", 0x00010001); + VarSetVersionHelper(pVariables, L"PROP16", MAKEQWORDVERSION(0, 0, 0, 0)); + VarSetVersionHelper(pVariables, L"PROP17", MAKEQWORDVERSION(1, 0, 0, 0)); + VarSetVersionHelper(pVariables, L"PROP18", MAKEQWORDVERSION(1, 1, 0, 0)); + VarSetVersionHelper(pVariables, L"PROP19", MAKEQWORDVERSION(1, 1, 1, 0)); + VarSetVersionHelper(pVariables, L"PROP20", MAKEQWORDVERSION(1, 1, 1, 1)); + VarSetNumericHelper(pVariables, L"vPROP21", 1); + VarSetVersionHelper(pVariables, L"PROP22", MAKEQWORDVERSION(65535, 65535, 65535, 65535)); + VarSetStringHelper(pVariables, L"PROP23", L"1.1.1"); + + // test conditions + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP7")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP8")); + Assert::True(EvaluateConditionHelper(pVariables, L"_PROP9")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP16")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"NONE = \"NOT\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 <> \"VAL1\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"NONE <> \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 ~= \"val1\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"val1\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 ~<> \"val1\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> \"val1\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 = 5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 = 0")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <> 5")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <> 0")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP10 = -10")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP10 <> -10")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 = v0")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 <> v1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 <> v0")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 = v0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 = v1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 = v1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP20 = v1.1.1.1")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.0")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"vPROP21 = 1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP23 = v1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 = PROP23")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> v1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 <> PROP1")); + + Assert::False(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775806")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775807")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 92233720368547758070000")); + + Assert::False(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775807")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -9223372036854775809")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -92233720368547758080000")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP22 = v65535.65535.65535.65535")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65536.65535.65535.65535")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65535.655350000.65535.65535")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 < 6")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 < 5")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 > 4")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 > 5")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 6")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <= 4")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 4")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 >= 6")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 << \"BEGIN\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 << \"END\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >> \"END\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >> \"BEGIN\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >< \"MID\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >< \"NONE\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 < v1.1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP16 < v0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 > v0.12")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 > v1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP18 >= v2.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1234.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1.1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.0.123")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP6 = \"6\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"\"6\" = PROP6")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP6 = \"ABC\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); + Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP13 << 1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP13 << 0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP14 >> 1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP14 >> 0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP15 >< 65537")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP15 >< 0")); + + Assert::False(EvaluateConditionHelper(pVariables, L"NOT PROP1")); + Assert::True(EvaluateConditionHelper(pVariables, L"NOT (PROP1 <> \"VAL1\")")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); + Assert::True(EvaluateConditionHelper(pVariables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); + + Assert::True(EvaluateFailureConditionHelper(pVariables, L"=")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1 = \"")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"1A")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"*")); + + Assert::True(EvaluateFailureConditionHelper(pVariables, L"1 == 1")); + } + finally + { + ReleaseVariables(pVariables); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj new file mode 100644 index 00000000..292cf28a --- /dev/null +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -0,0 +1,73 @@ + + + + + + + + Debug + Win32 + + + Release + Win32 + + + + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + {AB7EE608-E5FB-42A5-831F-0DEEEA141223} + DUtilUnitTests + ManagedCProj + DynamicLibrary + Unicode + true + + + + $(WixRoot)src\libs\dutil\inc + rpcrt4.lib;dutil.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(XunitPath)\xunit.dll + + + + + + {95BABD97-FBDB-453A-AF8A-FA031A07B599} + WixCppCliTestTools + + + {55CB1042-647B-4347-9876-3EA607AF8DCE} + WixTestTools + + + + diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters new file mode 100644 index 00000000..a83db35d --- /dev/null +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -0,0 +1,77 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DictUtilTest.cpp b/src/test/DUtilUnitTest/DictUtilTest.cpp new file mode 100644 index 00000000..fd8a5953 --- /dev/null +++ b/src/test/DUtilUnitTest/DictUtilTest.cpp @@ -0,0 +1,187 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +const DWORD numIterations = 100000; + +namespace DutilTests +{ + struct Value + { + DWORD dwNum; + LPWSTR sczKey; + }; + + public ref class DictUtil + { + public: + [Fact] + void DictUtilTest() + { + EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations); + + EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); + + StringListTestHelper(DICT_FLAG_NONE, numIterations); + + StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); + } + + private: + void EmbeddedKeyTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) + { + HRESULT hr = S_OK; + Value *rgValues = NULL; + Value *valueFound = NULL; + DWORD cValues = 0; + LPWSTR sczExpectedKey = NULL; + STRINGDICT_HANDLE sdValues = NULL; + + try + { + hr = DictCreateWithEmbeddedKey(&sdValues, 0, (void **)&rgValues, offsetof(Value, sczKey), dfFlags); + NativeAssert::Succeeded(hr, "Failed to create dictionary of values"); + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + cValues++; + + hr = MemEnsureArraySize((void **)&rgValues, cValues, sizeof(Value), 5); + NativeAssert::Succeeded(hr, "Failed to grow value array"); + + hr = StrAllocFormatted(&rgValues[i].sczKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); + + hr = DictAddValue(sdValues, rgValues + i); + NativeAssert::Succeeded(hr, "Failed to add item {0} to dict", i); + } + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey); + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + + if (dfFlags & DICT_FLAG_CASEINSENSITIVE) + { + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey, TRUE); + } + else + { + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "This embedded key is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); + } + } + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); + } + } + } + finally + { + for (DWORD i = 0; i < cValues; ++i) + { + ReleaseStr(rgValues[i].sczKey); + } + ReleaseMem(rgValues); + ReleaseStr(sczExpectedKey); + ReleaseDict(sdValues); + } + + LExit: + return; + } + + void StringListTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) + { + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + LPWSTR sczExpectedKey = NULL; + STRINGDICT_HANDLE sdValues = NULL; + + try + { + hr = DictCreateStringList(&sdValues, 0, dfFlags); + NativeAssert::Succeeded(hr, "Failed to create dictionary of keys"); + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); + + hr = DictAddKey(sdValues, sczKey); + NativeAssert::Succeeded(hr, "Failed to add key {0} to dict", i); + } + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + if (dfFlags & DICT_FLAG_CASEINSENSITIVE) + { + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + } + else + { + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "This stringlist dict is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); + } + } + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); + } + } + } + finally + { + ReleaseStr(sczKey); + ReleaseStr(sczExpectedKey); + ReleaseDict(sdValues); + } + + LExit: + return; + } + }; +} diff --git a/src/test/DUtilUnitTest/DirUtilTests.cpp b/src/test/DUtilUnitTest/DirUtilTests.cpp new file mode 100644 index 00000000..a965c3d5 --- /dev/null +++ b/src/test/DUtilUnitTest/DirUtilTests.cpp @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + public ref class DirUtil + { + public: + [Fact] + void DirUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczCurrentDir = NULL; + LPWSTR sczGuid = NULL; + LPWSTR sczFolder = NULL; + LPWSTR sczSubFolder = NULL; + + try + { + hr = GuidCreate(&sczGuid); + NativeAssert::Succeeded(hr, "Failed to create guid."); + + hr = DirGetCurrent(&sczCurrentDir); + NativeAssert::Succeeded(hr, "Failed to get current directory."); + + hr = PathConcat(sczCurrentDir, sczGuid, &sczFolder); + NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid); + + BOOL fExists = DirExists(sczFolder, NULL); + Assert::False(fExists); + + hr = PathConcat(sczFolder, L"foo", &sczSubFolder); + NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder); + + hr = DirEnsureExists(sczSubFolder, NULL); + NativeAssert::Succeeded(hr, "Failed to create multiple directories: %ls", sczSubFolder); + + // Test failure to delete non-empty folder. + hr = DirEnsureDelete(sczFolder, FALSE, FALSE); + Assert::Equal(HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY), hr); + + hr = DirEnsureDelete(sczSubFolder, FALSE, FALSE); + NativeAssert::Succeeded(hr, "Failed to delete single directory: %ls", sczSubFolder); + + // Put the directory back and we'll test deleting tree. + hr = DirEnsureExists(sczSubFolder, NULL); + NativeAssert::Succeeded(hr, "Failed to create single directory: %ls", sczSubFolder); + + hr = DirEnsureDelete(sczFolder, FALSE, TRUE); + NativeAssert::Succeeded(hr, "Failed to delete directory tree: %ls", sczFolder); + + // Finally, try to create "C:\" which would normally fail, but we want success + hr = DirEnsureExists(L"C:\\", NULL); + NativeAssert::Succeeded(hr, "Failed to create C:\\"); + } + finally + { + ReleaseStr(sczSubFolder); + ReleaseStr(sczFolder); + ReleaseStr(sczGuid); + ReleaseStr(sczCurrentDir); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp new file mode 100644 index 00000000..41638bdb --- /dev/null +++ b/src/test/DUtilUnitTest/FileUtilTest.cpp @@ -0,0 +1,118 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + public ref class FileUtil + { + public: + [Fact(Skip="Skipped until we have a good way to reference ANSI.txt.")] + void FileUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczTempDir = NULL; + LPWSTR sczFileDir = NULL; + + try + { + hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get temp dir"); + + hr = PathExpand(&sczFileDir, L"%WIX_ROOT%\\examples\\data\\TextEncodings\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to encodings file dir"); + + hr = DirEnsureExists(sczTempDir, NULL); + NativeAssert::Succeeded(hr, "Failed to ensure directory exists: {0}", sczTempDir); + + TestFile(sczFileDir, sczTempDir, L"ANSI.txt", 32, FILE_ENCODING_UTF8); + // Big endian not supported today! + //TestFile(sczFileDir, L"UnicodeBENoBOM.txt", 34); + //TestFile(sczFileDir, L"UnicodeBEWithBOM.txt", 34); + TestFile(sczFileDir, sczTempDir, L"UnicodeLENoBOM.txt", 34, FILE_ENCODING_UTF16); + TestFile(sczFileDir, sczTempDir, L"UnicodeLEWithBOM.txt", 34, FILE_ENCODING_UTF16_WITH_BOM); + TestFile(sczFileDir, sczTempDir, L"UTF8WithSignature.txt", 34, FILE_ENCODING_UTF8_WITH_BOM); + + hr = DirEnsureDelete(sczTempDir, TRUE, TRUE); + } + finally + { + ReleaseStr(sczTempDir); + ReleaseStr(sczFileDir); + } + } + + private: + void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, DWORD dwExpectedStringLength, FILE_ENCODING feExpectedEncoding) + { + HRESULT hr = S_OK; + LPWSTR sczFullPath = NULL; + LPWSTR sczContents = NULL; + LPWSTR sczOutputPath = NULL; + FILE_ENCODING feEncodingFound = FILE_ENCODING_UNSPECIFIED; + BYTE *pbFile1 = NULL; + DWORD cbFile1 = 0; + BYTE *pbFile2 = NULL; + DWORD cbFile2 = 0; + + try + { + hr = PathConcat(wzDir, wzFileName, &sczFullPath); + NativeAssert::Succeeded(hr, "Failed to create path to test file: {0}", sczFullPath); + + hr = FileToString(sczFullPath, &sczContents, &feEncodingFound); + hr = E_FAIL; + NativeAssert::Succeeded(hr, "Failed to read text from file: {0}", sczFullPath); + + if (!sczContents) + { + hr = E_FAIL; + NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath); + } + + if ((DWORD)lstrlenW(sczContents) != dwExpectedStringLength) + { + hr = E_FAIL; + ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %u, found size %u)", sczFullPath, dwExpectedStringLength, lstrlenW(sczContents)); + } + + if (feEncodingFound != feExpectedEncoding) + { + hr = E_FAIL; + ExitOnFailure(hr, "FileToString() returned unexpected encoding type for file: %ls (expected type %u, found type %u)", sczFullPath, feExpectedEncoding, feEncodingFound); + } + + hr = PathConcat(wzTempDir, wzFileName, &sczOutputPath); + NativeAssert::Succeeded(hr, "Failed to get output path"); + + hr = FileFromString(sczOutputPath, 0, sczContents, feExpectedEncoding); + NativeAssert::Succeeded(hr, "Failed to write contents of file back out to disk"); + + hr = FileRead(&pbFile1, &cbFile1, sczFullPath); + NativeAssert::Succeeded(hr, "Failed to read input file as binary"); + + hr = FileRead(&pbFile2, &cbFile2, sczOutputPath); + NativeAssert::Succeeded(hr, "Failed to read output file as binary"); + + if (cbFile1 != cbFile2 || 0 != memcmp(pbFile1, pbFile2, cbFile1)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Outputted file doesn't match input file: \"%ls\" and \"%ls\"", sczFullPath, sczOutputPath); + } + } + finally + { + ReleaseStr(sczOutputPath); + ReleaseStr(sczFullPath); + ReleaseStr(sczContents); + } + + LExit: + return; + } + }; +} diff --git a/src/test/DUtilUnitTest/GuidUtilTest.cpp b/src/test/DUtilUnitTest/GuidUtilTest.cpp new file mode 100644 index 00000000..d0ea9a89 --- /dev/null +++ b/src/test/DUtilUnitTest/GuidUtilTest.cpp @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + public ref class GuidUtil + { + public: + [Fact] + void GuidCreateTest() + { + HRESULT hr = S_OK; + WCHAR wzGuid1[GUID_STRING_LENGTH]; + WCHAR wzGuid2[GUID_STRING_LENGTH]; + + hr = GuidFixedCreate(wzGuid1); + NativeAssert::Succeeded(hr, "Failed to create first guid."); + Guid firstGuid = Guid::Parse(gcnew String(wzGuid1)); + + hr = GuidFixedCreate(wzGuid2); + NativeAssert::Succeeded(hr, "Failed to create second guid."); + Guid secondGuid = Guid::Parse(gcnew String(wzGuid2)); + + NativeAssert::NotStringEqual(wzGuid1, wzGuid2); + NativeAssert::NotEqual(firstGuid, secondGuid); + } + + [Fact] + void GuidCreateSczTest() + { + HRESULT hr = S_OK; + LPWSTR sczGuid1 = NULL; + LPWSTR sczGuid2 = NULL; + + try + { + hr = GuidCreate(&sczGuid1); + NativeAssert::Succeeded(hr, "Failed to create first guid."); + Guid firstGuid = Guid::Parse(gcnew String(sczGuid1)); + + hr = GuidCreate(&sczGuid2); + NativeAssert::Succeeded(hr, "Failed to create second guid."); + Guid secondGuid = Guid::Parse(gcnew String(sczGuid2)); + + NativeAssert::NotStringEqual(sczGuid1, sczGuid2); + NativeAssert::NotEqual(firstGuid, secondGuid); + } + finally + { + ReleaseStr(sczGuid1); + ReleaseStr(sczGuid2); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/IniUtilTest.cpp b/src/test/DUtilUnitTest/IniUtilTest.cpp new file mode 100644 index 00000000..e28f357e --- /dev/null +++ b/src/test/DUtilUnitTest/IniUtilTest.cpp @@ -0,0 +1,342 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +typedef HRESULT (__clrcall *IniFormatParameters)( + INI_HANDLE + ); + +namespace DutilTests +{ + public ref class IniUtil + { + public: + [Fact] + void IniUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczTempIniFilePath = NULL; + LPWSTR sczTempIniFileDir = NULL; + 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"; + 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"; + + try + { + hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to temp INI file"); + + hr = PathGetDirectory(sczTempIniFilePath, &sczTempIniFileDir); + NativeAssert::Succeeded(hr, "Failed to get directory to temp INI file"); + + hr = DirEnsureDelete(sczTempIniFileDir, TRUE, TRUE); + if (E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + NativeAssert::Succeeded(hr, "Failed to delete IniUtilTest directory: {0}", sczTempIniFileDir); + + hr = DirEnsureExists(sczTempIniFileDir, NULL); + NativeAssert::Succeeded(hr, "Failed to ensure temp directory exists: {0}", sczTempIniFileDir); + + // Tests parsing, then modifying a regular INI file + TestReadThenWrite(sczTempIniFilePath, StandardIniFormat, wzIniContents); + + // Tests programmatically creating from scratch, then parsing an INI file + TestWriteThenRead(sczTempIniFilePath, StandardIniFormat); + + // Tests parsing, then modifying a regular INI file + TestReadThenWrite(sczTempIniFilePath, ScriptFormat, wzScriptContents); + + // Tests programmatically creating from scratch, then parsing an INI file + TestWriteThenRead(sczTempIniFilePath, ScriptFormat); + } + finally + { + ReleaseStr(sczTempIniFilePath); + ReleaseStr(sczTempIniFileDir); + } + } + + private: + void AssertValue(INI_HANDLE iniHandle, LPCWSTR wzValueName, LPCWSTR wzValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + try + { + hr = IniGetValue(iniHandle, wzValueName, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get ini value: {0}", wzValueName); + + if (0 != wcscmp(sczValue, wzValue)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Expected to find value in INI: '%ls'='%ls' - but found value '%ls' instead", wzValueName, wzValue, sczValue); + } + } + finally + { + ReleaseStr(sczValue); + } + + LExit: + return; + } + + void AssertNoValue(INI_HANDLE iniHandle, LPCWSTR wzValueName) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + try + { + hr = IniGetValue(iniHandle, wzValueName, &sczValue); + if (E_NOTFOUND != hr) + { + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "INI value shouldn't have been found: %ls", wzValueName); + } + } + finally + { + ReleaseStr(sczValue); + } + + LExit: + return; + } + + static HRESULT StandardIniFormat(__inout INI_HANDLE iniHandle) + { + HRESULT hr = S_OK; + + hr = IniSetOpenTag(iniHandle, L"[", L"]"); + NativeAssert::Succeeded(hr, "Failed to set open tag settings on ini handle"); + + hr = IniSetValueStyle(iniHandle, NULL, L"="); + NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); + + hr = IniSetCommentStyle(iniHandle, L";"); + NativeAssert::Succeeded(hr, "Failed to set comment style setting on ini handle"); + + return hr; + } + + static HRESULT ScriptFormat(__inout INI_HANDLE iniHandle) + { + HRESULT hr = S_OK; + + hr = IniSetValueStyle(iniHandle, L"setf ~", L" "); + NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); + + return hr; + } + + void TestReadThenWrite(LPWSTR wzIniFilePath, IniFormatParameters SetFormat, LPCWSTR wzContents) + { + HRESULT hr = S_OK; + INI_HANDLE iniHandle = NULL; + INI_HANDLE iniHandle2 = NULL; + INI_VALUE *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = FileWrite(wzIniFilePath, 0, reinterpret_cast(wzContents), lstrlenW(wzContents) * sizeof(WCHAR), NULL); + NativeAssert::Succeeded(hr, "Failed to write out INI file"); + + hr = IniInitialize(&iniHandle); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(5, cValues); + + AssertValue(iniHandle, L"PlainValue", L"Blah"); + AssertNoValue(iniHandle, L"PlainValue2"); + AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); + AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); + AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + + hr = IniSetValue(iniHandle, L"PlainValue2", L"Blah2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(7, cValues); + + AssertValue(iniHandle, L"PlainValue", L"Blah"); + AssertValue(iniHandle, L"PlainValue2", L"Blah2"); + AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); + AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); + AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); + + // Try deleting a value as well + hr = IniSetValue(iniHandle, L"Section1\\Section1ValueB", NULL); + NativeAssert::Succeeded(hr, "Failed to kill value in INI"); + + hr = IniWriteFile(iniHandle, NULL, FILE_ENCODING_UNSPECIFIED); + NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); + + ReleaseNullIni(iniHandle); + // Now re-parse the INI we just wrote and make sure it matches the values we expect + hr = IniInitialize(&iniHandle2); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle2); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle2, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle2, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(6, cValues); + + AssertValue(iniHandle2, L"PlainValue", L"Blah"); + AssertValue(iniHandle2, L"PlainValue2", L"Blah2"); + AssertValue(iniHandle2, L"Section1\\Section1ValueA", L"Foo"); + AssertNoValue(iniHandle2, L"Section1\\Section1ValueB"); + AssertValue(iniHandle2, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle2, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle2, L"Section1\\CreatedValue", L"Woo"); + AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arrmod"); + } + finally + { + ReleaseIni(iniHandle); + ReleaseIni(iniHandle2); + } + } + + void TestWriteThenRead(LPWSTR wzIniFilePath, IniFormatParameters SetFormat) + { + HRESULT hr = S_OK; + INI_HANDLE iniHandle = NULL; + INI_HANDLE iniHandle2 = NULL; + INI_VALUE *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = FileEnsureDelete(wzIniFilePath); + NativeAssert::Succeeded(hr, "Failed to ensure file is deleted"); + + hr = IniInitialize(&iniHandle); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(0, cValues); + + hr = IniSetValue(iniHandle, L"Value1", L"BlahTypo"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value2", L"Blah2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value3", L"Blah3"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value4", L"Blah4"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value4", NULL); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value1", L"Blah1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(8, cValues); + + AssertValue(iniHandle, L"Value1", L"Blah1"); + AssertValue(iniHandle, L"Value2", L"Blah2"); + AssertValue(iniHandle, L"Value3", L"Blah3"); + AssertNoValue(iniHandle, L"Value4"); + AssertValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); + AssertValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); + AssertValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + + hr = IniWriteFile(iniHandle, wzIniFilePath, FILE_ENCODING_UNSPECIFIED); + NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); + + ReleaseNullIni(iniHandle); + // Now re-parse the INI we just wrote and make sure it matches the values we expect + hr = IniInitialize(&iniHandle2); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle2); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle2, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle2, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(7, cValues); + + AssertValue(iniHandle2, L"Value1", L"Blah1"); + AssertValue(iniHandle2, L"Value2", L"Blah2"); + AssertValue(iniHandle2, L"Value3", L"Blah3"); + AssertNoValue(iniHandle2, L"Value4"); + AssertValue(iniHandle2, L"Section1\\Value1", L"Section1Value1"); + AssertValue(iniHandle2, L"Section1\\Value2", L"Section1Value2"); + AssertValue(iniHandle2, L"Section2\\Value1", L"Section2Value1"); + AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arr"); + } + finally + { + ReleaseIni(iniHandle); + ReleaseIni(iniHandle2); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/MemUtilTest.cpp b/src/test/DUtilUnitTest/MemUtilTest.cpp new file mode 100644 index 00000000..6dec9682 --- /dev/null +++ b/src/test/DUtilUnitTest/MemUtilTest.cpp @@ -0,0 +1,491 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + struct ArrayValue + { + DWORD dwNum; + void *pvNull1; + LPWSTR sczString; + void *pvNull2; + }; + + public ref class MemUtil + { + public: + [Fact] + void MemUtilAppendTest() + { + HRESULT hr = S_OK; + DWORD dwSize; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 1"); + ++cValues; + SetItem(rgValues + 0, 0); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 2"); + ++cValues; + SetItem(rgValues + 1, 1); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 3"); + ++cValues; + SetItem(rgValues + 2, 2); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 4"); + ++cValues; + SetItem(rgValues + 3, 3); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 5"); + ++cValues; + SetItem(rgValues + 4, 4); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 6"); + ++cValues; + SetItem(rgValues + 5, 5); + + // OK, we used growth size 5, so let's try ensuring we have space for 6 (5 + first item) items + // and make sure it doesn't grow since we already have enough space + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to ensure array size matches what it should already be"); + dwSize = MemSize(rgValues); + if (dwSize != 6 * sizeof(ArrayValue)) + { + hr = E_FAIL; + ExitOnFailure(hr, "MemEnsureArraySize is growing an array that is already big enough!"); + } + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 6, 6); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 7, 7); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 8, 8); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + } + + LExit: + return; + } + + [Fact] + void MemUtilInsertTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of empty array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 5); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert at end of array"); + ++cValues; + CheckNullItem(rgValues + 1); + SetItem(rgValues + 1, 6); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 4); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 3); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 1); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 1); + SetItem(rgValues + 1, 2); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 0); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + CheckNullItem(rgValues + 7); + SetItem(rgValues + 7, 7); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 8, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 8); + SetItem(rgValues + 8, 8); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + } + } + + [Fact] + void MemUtilRemovePreserveOrderTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Remove last item + MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), TRUE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove last two items + MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove first item + MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), TRUE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i + 1); + } + + + // Remove first two items + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i + 3); + } + + // Remove middle two items + MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + CheckItem(rgValues, 3); + CheckItem(rgValues + 1, 6); + + // Remove last 2 items to ensure we don't crash + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + } + finally + { + ReleaseMem(rgValues); + } + } + + [Fact] + void MemUtilRemoveFastTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Remove last item + MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), FALSE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove last two items + MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove first item + MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), FALSE); + --cValues; + + CheckItem(rgValues, 6); + CheckItem(rgValues + 1, 1); + CheckItem(rgValues + 2, 2); + CheckItem(rgValues + 3, 3); + CheckItem(rgValues + 4, 4); + CheckItem(rgValues + 5, 5); + + // Remove first two items + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + CheckItem(rgValues, 4); + CheckItem(rgValues + 1, 5); + CheckItem(rgValues + 2, 2); + CheckItem(rgValues + 3, 3); + + + // Remove middle two items + MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + CheckItem(rgValues, 4); + CheckItem(rgValues + 1, 3); + + // Remove last 2 items to ensure we don't crash + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + } + finally + { + ReleaseMem(rgValues); + } + } + + [Fact] + void MemUtilSwapTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Swap first two + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 1); + CheckItem(rgValues + 1, 0); + for (DWORD i = 2; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap them back + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap first and last items (index 0 and 9) + MemArraySwapItems(rgValues, 0, 9, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 9, 0); + for (DWORD i = 1; i < cValues - 1; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 1 and 8 + MemArraySwapItems(rgValues, 1, 8, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 1, 8); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 2; i < cValues - 2; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 2 and 7 + MemArraySwapItems(rgValues, 2, 7, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 1, 8); + CheckItem(rgValues + 2, 7); + CheckItem(rgValues + 7, 2); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 3; i < cValues - 3; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 0 and 1 + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 8); + CheckItem(rgValues + 1, 9); + CheckItem(rgValues + 2, 7); + CheckItem(rgValues + 7, 2); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 3; i < cValues - 3; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + } + } + + private: + void SetItem(ArrayValue *pValue, DWORD dwValue) + { + HRESULT hr = S_OK; + pValue->dwNum = dwValue; + + hr = StrAllocFormatted(&pValue->sczString, L"%u", dwValue); + NativeAssert::Succeeded(hr, "Failed to allocate string"); + } + + void CheckItem(ArrayValue *pValue, DWORD dwValue) + { + HRESULT hr = S_OK; + LPWSTR sczTemp = NULL; + + try + { + NativeAssert::Equal(dwValue, pValue->dwNum); + + hr = StrAllocFormatted(&sczTemp, L"%u", dwValue); + NativeAssert::Succeeded(hr, "Failed to allocate temp string"); + + NativeAssert::StringEqual(sczTemp, pValue->sczString, TRUE); + + if (pValue->pvNull1 || pValue->pvNull2) + { + hr = E_FAIL; + ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); + } + } + finally + { + ReleaseStr(sczTemp); + } + + LExit: + return; + } + + void CheckNullItem(ArrayValue *pValue) + { + HRESULT hr = S_OK; + + NativeAssert::Equal(0, pValue->dwNum); + + if (pValue->sczString) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item found isn't NULL!"); + } + + if (pValue->pvNull1 || pValue->pvNull2) + { + hr = E_FAIL; + ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); + } + + LExit: + return; + } + }; +} diff --git a/src/test/DUtilUnitTest/MonUtilTest.cpp b/src/test/DUtilUnitTest/MonUtilTest.cpp new file mode 100644 index 00000000..a6ed32f1 --- /dev/null +++ b/src/test/DUtilUnitTest/MonUtilTest.cpp @@ -0,0 +1,487 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#undef RemoveDirectory + +using namespace System; +using namespace System::Collections::Generic; +using namespace System::Runtime::InteropServices; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + const int PREWAIT = 20; + const int POSTWAIT = 480; + const int FULLWAIT = 500; + const int SILENCEPERIOD = 100; + + struct RegKey + { + HRESULT hr; + HKEY hkRoot; + LPCWSTR wzSubKey; + REG_KEY_BITNESS kbKeyBitness; + BOOL fRecursive; + }; + struct Directory + { + HRESULT hr; + LPCWSTR wzPath; + BOOL fRecursive; + }; + struct Results + { + RegKey *rgRegKeys; + DWORD cRegKeys; + Directory *rgDirectories; + DWORD cDirectories; + }; + + public delegate void MonGeneralDelegate(HRESULT, LPVOID); + + public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID); + + public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID); + + public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID); + + static void MonGeneral( + __in HRESULT /*hrResult*/, + __in_opt LPVOID /*pvContext*/ + ) + { + Assert::True(false); + } + + static void MonDriveStatus( + __in WCHAR /*chDrive*/, + __in BOOL /*fArriving*/, + __in_opt LPVOID /*pvContext*/ + ) + { + } + + static void MonDirectory( + __in HRESULT hrResult, + __in_z LPCWSTR wzPath, + __in_z BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvDirectoryContext + ) + { + Assert::Equal(S_OK, hrResult); + Assert::Equal(0, reinterpret_cast(pvDirectoryContext)); + + HRESULT hr = S_OK; + Results *pResults = reinterpret_cast(pvContext); + + hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5); + NativeAssert::ValidReturnCode(hr, S_OK); + ++pResults->cDirectories; + + pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult; + pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath; + pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive; + } + + static void MonRegKey( + __in HRESULT hrResult, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in_z BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvRegKeyContext + ) + { + Assert::Equal(S_OK, hrResult); + Assert::Equal(0, reinterpret_cast(pvRegKeyContext)); + + HRESULT hr = S_OK; + Results *pResults = reinterpret_cast(pvContext); + + hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5); + NativeAssert::ValidReturnCode(hr, S_OK); + ++pResults->cRegKeys; + + pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult; + pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot; + pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey; + pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness; + pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive; + } + + public ref class MonUtil + { + public: + void ClearResults(Results *pResults) + { + ReleaseNullMem(pResults->rgDirectories); + pResults->cDirectories = 0; + ReleaseNullMem(pResults->rgRegKeys); + pResults->cRegKeys = 0; + } + + void RemoveDirectory(LPCWSTR wzPath) + { + DWORD dwRetryCount = 0; + const DWORD c_dwMaxRetryCount = 100; + const DWORD c_dwRetryInterval = 50; + + HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE); + + // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed + // (and deletion will be "pending" until our monitor handle is closed) + // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete() + // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later + // (after the waiter thread wakes up, it will release the handle) + while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount) + { + ::Sleep(c_dwRetryInterval); + ++dwRetryCount; + hr = DirEnsureDelete(wzPath, TRUE, TRUE); + } + + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + } + + void TestDirectory(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPWSTR sczShallowPath = NULL; + LPWSTR sczParentPath = NULL; + LPWSTR sczDeepPath = NULL; + LPWSTR sczChildPath = NULL; + LPWSTR sczChildFilePath = NULL; + + try + { + hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + RemoveDirectory(sczShallowPath); + + hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = DirEnsureExists(sczParentPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + // Make sure creating the parent directory does nothing, even after silence period + ::Sleep(FULLWAIT); + Assert::Equal(0, pResults->cDirectories); + + // Now create the target path, no notification until after the silence period + hr = DirEnsureExists(sczDeepPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(0, pResults->cDirectories); + + // Now after the full silence period, it should have triggered + ::Sleep(POSTWAIT); + Assert::Equal(1, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK); + + // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. + RemoveDirectory(sczShallowPath); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK); + + // Create the parent directory again, still should be nothing even after full silence period + hr = DirEnsureExists(sczParentPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + + hr = DirEnsureExists(sczChildPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(2, pResults->cDirectories); + + ::Sleep(POSTWAIT); + Assert::Equal(3, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); + + // Write a file to a deep child subfolder, and make sure it's detected + hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + ::Sleep(PREWAIT); + Assert::Equal(3, pResults->cDirectories); + + ::Sleep(POSTWAIT); + Assert::Equal(4, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); + + RemoveDirectory(sczParentPath); + + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); + + // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked + hr = MonRemoveDirectory(handle, sczDeepPath, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + ::Sleep(PREWAIT); + + hr = DirEnsureExists(sczDeepPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); + + // Finally, add it back so we can test multiple things to monitor at once + hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + finally + { + ReleaseStr(sczShallowPath); + ReleaseStr(sczDeepPath); + ReleaseStr(sczParentPath); + } + } + + void TestRegKey(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\"; + LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\"; + LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\"; + LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\"; + HKEY hk = NULL; + + try + { + hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + + hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + ReleaseRegKey(hk); + // Make sure creating the parent key does nothing, even after silence period + ::Sleep(FULLWAIT); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + Assert::Equal(0, pResults->cRegKeys); + + // Now create the target path, no notification until after the silence period + hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(PREWAIT); + Assert::Equal(0, pResults->cRegKeys); + + // Now after the full silence period, it should have triggered + ::Sleep(POSTWAIT); + Assert::Equal(1, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK); + + // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. + hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + ::Sleep(PREWAIT); + Assert::Equal(1, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK); + + // Create the parent directory again, still should be nothing even after full silence period + hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cRegKeys); + + hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(2, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(3, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); + + // Write a registry value to some deep child subkey, and make sure it's detected + hr = RegWriteString(hk, L"valuename", L"testvalue"); + NativeAssert::ValidReturnCode(hr, S_OK); + ReleaseRegKey(hk); + ::Sleep(PREWAIT); + Assert::Equal(3, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(4, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); + + hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cRegKeys); + + // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked + hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cRegKeys); + } + finally + { + ReleaseRegKey(hk); + } + } + + void TestMoreThan64(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPWSTR sczBaseDir = NULL; + LPWSTR sczDir = NULL; + LPWSTR sczFile = NULL; + + try + { + hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + for (DWORD i = 0; i < 200; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = DirEnsureExists(sczDir, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + + hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + + hr = PathConcat(sczDir, L"file.txt", &sczFile); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(1, pResults->cDirectories); + + for (DWORD i = 0; i < 199; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = MonRemoveDirectory(handle, sczDir, FALSE); + NativeAssert::ValidReturnCode(hr, S_OK); + } + ::Sleep(FULLWAIT); + + hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + + for (DWORD i = 0; i < 199; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + ::Sleep(FULLWAIT); + + hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(3, pResults->cDirectories); + } + finally + { + ReleaseStr(sczBaseDir); + ReleaseStr(sczDir); + ReleaseStr(sczFile); + } + } + + [Fact] + void MonUtilTest() + { + HRESULT hr = S_OK; + MON_HANDLE handle = NULL; + List^ gcHandles = gcnew List(); + Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE); + Assert::True(NULL != pResults); + + try + { + // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild + MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral); + GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral); + gcHandles->Add(gchMonGeneral); + IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral); + + MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus); + GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus); + gcHandles->Add(gchMonDriveStatus); + IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus); + + MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory); + GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory); + gcHandles->Add(gchMonDirectory); + IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory); + + MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey); + GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey); + gcHandles->Add(gchMonRegKey); + IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey); + + // "Silence period" is 100 ms + hr = MonCreate(&handle, static_cast(ipMonGeneral.ToPointer()), static_cast(ipMonDriveStatus.ToPointer()), static_cast(ipMonDirectory.ToPointer()), static_cast(ipMonRegKey.ToPointer()), pResults); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegInitialize(); + NativeAssert::ValidReturnCode(hr, S_OK); + + TestDirectory(handle, pResults); + ClearResults(pResults); + TestRegKey(handle, pResults); + ClearResults(pResults); + TestMoreThan64(handle, pResults); + ClearResults(pResults); + } + finally + { + ReleaseMon(handle); + + for each (GCHandle gcHandle in gcHandles) + { + gcHandle.Free(); + } + + ReleaseMem(pResults->rgDirectories); + ReleaseMem(pResults->rgRegKeys); + ReleaseMem(pResults); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/NativeAssert.h b/src/test/DUtilUnitTest/NativeAssert.h new file mode 100644 index 00000000..b10910c0 --- /dev/null +++ b/src/test/DUtilUnitTest/NativeAssert.h @@ -0,0 +1,83 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace WixTest { + + using namespace System; + using namespace System::Collections::Generic; + using namespace System::Linq; + using namespace Xunit; + + public ref class NativeAssert : WixAssert + { + public: + static void NotNull(LPCWSTR wz) + { + if (!wz) + { + Assert::NotNull(nullptr); + } + } + + // For some reason, naming these NotStringEqual methods "NotEqual" breaks Intellisense in files that call any overload of the NotEqual method. + static void NotStringEqual(LPCWSTR expected, LPCWSTR actual) + { + NativeAssert::NotStringEqual(expected, actual, FALSE); + } + + static void NotStringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) + { + IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; + Assert::NotEqual(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); + } + + // For some reason, naming these StringEqual methods "Equal" breaks Intellisense in files that call any overload of the Equal method. + static void StringEqual(LPCWSTR expected, LPCWSTR actual) + { + NativeAssert::StringEqual(expected, actual, FALSE); + } + + static void StringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) + { + IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; + Assert::Equal(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); + } + + static void Succeeded(HRESULT hr, LPCSTR zFormat, LPCSTR zArg, ... array^ zArgs) + { + array^ formatArgs = gcnew array(zArgs->Length + 1); + formatArgs[0] = NativeAssert::LPSTRToString(zArg); + for (int i = 0; i < zArgs->Length; ++i) + { + formatArgs[i + 1] = NativeAssert::LPSTRToString(zArgs[i]); + } + WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); + } + + static void Succeeded(HRESULT hr, LPCSTR zFormat, ... array^ wzArgs) + { + array^ formatArgs = gcnew array(wzArgs->Length); + for (int i = 0; i < wzArgs->Length; ++i) + { + formatArgs[i] = NativeAssert::LPWSTRToString(wzArgs[i]); + } + WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); + } + + static void ValidReturnCode(HRESULT hr, ... array^ validReturnCodes) + { + Assert::Contains(hr, (IEnumerable^)validReturnCodes); + } + + private: + static String^ LPSTRToString(LPCSTR z) + { + return z ? gcnew String(z) : nullptr; + } + static String^ LPWSTRToString(LPCWSTR wz) + { + return wz ? gcnew String(wz) : nullptr; + } + }; +} diff --git a/src/test/DUtilUnitTest/PathUtilTest.cpp b/src/test/DUtilUnitTest/PathUtilTest.cpp new file mode 100644 index 00000000..13ec3be3 --- /dev/null +++ b/src/test/DUtilUnitTest/PathUtilTest.cpp @@ -0,0 +1,80 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + public ref class PathUtil + { + public: + [Fact] + void PathGetHierarchyArrayTest() + { + HRESULT hr = S_OK; + LPWSTR *rgsczPaths = NULL; + UINT cPaths = 0; + + try + { + hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\a.txt", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path"); + Assert::Equal(5, cPaths); + NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\a.txt", rgsczPaths[4]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path"); + Assert::Equal(4, cPaths); + NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\file.txt", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path"); + Assert::Equal(3, cPaths); + NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\file.txt", rgsczPaths[2]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(2, cPaths); + NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\ValueName", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(4, cPaths); + NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\ValueName", rgsczPaths[3]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(3, cPaths); + NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); + ReleaseNullStrArray(rgsczPaths, cPaths); + } + finally + { + ReleaseStrArray(rgsczPaths, cPaths); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/SceUtilTest.cpp b/src/test/DUtilUnitTest/SceUtilTest.cpp new file mode 100644 index 00000000..75b9222a --- /dev/null +++ b/src/test/DUtilUnitTest/SceUtilTest.cpp @@ -0,0 +1,488 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include +#include + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);}; + +namespace DutilTests +{ + enum TABLES + { + TABLE_A, + TABLE_COUNT + }; + + enum TABLE_A_COLUMNS + { + TABLE_A_KEY, + TABLE_A_BINARY, + TABLE_A_DWORD, + TABLE_A_QWORD, + TABLE_A_BOOL, + TABLE_A_STRING, + TABLE_A_DWORD_NULLABLE, + TABLE_A_INITIAL_COLUMNS, + + TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS, + TABLE_A_FINAL_COLUMNS + }; + + struct TableARowValue + { + DWORD dwAutoGenKey; + + BYTE *pbBinary; + DWORD cBinary; + + DWORD dw; + DWORD64 qw; + BOOL f; + LPWSTR scz; + + BOOL fNullablePresent; + DWORD dwNullable; + + BOOL fSchemaV2; + LPWSTR sczExtra; + }; + + public ref class SceUtil + { + public: + void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema) + { + DWORD dwTable; + + for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns); + ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes); + } + + ReleaseMem(pdsSchema->rgTables); + + return; + } + + void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended) + { + pSchema->cTables = TABLE_COUNT; + pSchema->rgTables = static_cast(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE)); + NativeAssert::True(pSchema->rgTables != NULL); + + pSchema->rgTables[TABLE_A].wzName = L"TableA"; + pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS; + pSchema->rgTables[TABLE_A].cIndexes = 2; + + for (DWORD i = 0; i < pSchema->cTables; ++i) + { + pSchema->rgTables[i].rgColumns = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE)); + NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL); + + pSchema->rgTables[i].rgIndexes = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE)); + NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL); + } + + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE; + + if (fIncludeExtended) + { + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE; + } + + static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD }; + static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING }; + + ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword"); + ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String"); + } + + void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra) + { + pValue->pbBinary = pbBinary; + pValue->cBinary = cBinary; + pValue->dw = dw; + pValue->qw = qw; + pValue->f = f; + pValue->scz = scz; + + if (pdw) + { + pValue->fNullablePresent = TRUE; + pValue->dwNullable = *pdw; + } + else + { + pValue->fNullablePresent = FALSE; + } + + if (sczExtra) + { + pValue->fSchemaV2 = TRUE; + pValue->sczExtra = sczExtra; + } + else + { + pValue->fSchemaV2 = FALSE; + } + } + + void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther) + { + NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary); + NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary)); + + NativeAssert::Equal(pValueExpected->dw, pValueOther->dw); + NativeAssert::Equal(pValueExpected->qw, pValueOther->qw); + NativeAssert::Equal(pValueExpected->f, pValueOther->f); + NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz)); + + NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent); + if (pValueExpected->fNullablePresent) + { + NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable); + } + + NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2); + if (pValueExpected->fSchemaV2) + { + NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra)); + } + } + + void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback) + { + HRESULT hr = S_OK; + SCE_ROW_HANDLE sceRow = NULL; + + hr = SceBeginTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to begin transaction"); + + hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow); + NativeAssert::Succeeded(hr, "Failed to prepare to insert row"); + + hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary); + NativeAssert::Succeeded(hr, "Failed to set binary value"); + + hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw); + NativeAssert::Succeeded(hr, "Failed to set dword value"); + + hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw); + NativeAssert::Succeeded(hr, "Failed to set qword value"); + + hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f); + NativeAssert::Succeeded(hr, "Failed to set bool value"); + + hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz); + NativeAssert::Succeeded(hr, "Failed to set string value"); + + if (pValue->fNullablePresent) + { + hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable); + NativeAssert::Succeeded(hr, "Failed to set dword value"); + } + else + { + hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE); + NativeAssert::Succeeded(hr, "Failed to set null value"); + } + + if (pValue->fSchemaV2) + { + hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra); + NativeAssert::Succeeded(hr, "Failed to set extra string value"); + } + + hr = SceFinishUpdate(sceRow); + NativeAssert::Succeeded(hr, "Failed to finish insert"); + + if (fRollback) + { + hr = SceRollbackTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to rollback transaction"); + } + else + { + hr = SceCommitTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to commit transaction"); + + hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey); + NativeAssert::Succeeded(hr, "Failed to get autogen key after insert"); + + NativeAssert::True(pValue->dwAutoGenKey != 0); + } + + ReleaseSceRow(sceRow); + } + + void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow) + { + HRESULT hr = S_OK; + TableARowValue value = {}; + + hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary); + NativeAssert::Succeeded(hr, "Failed to get binary value from result row"); + + hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw); + NativeAssert::Succeeded(hr, "Failed to get dword value from result row"); + + hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw); + NativeAssert::Succeeded(hr, "Failed to get qword value from result row"); + + hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f); + NativeAssert::Succeeded(hr, "Failed to get bool value from result row"); + + hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz); + NativeAssert::Succeeded(hr, "Failed to get string value from result row"); + + hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable); + if (hr == E_NOTFOUND) + { + value.fNullablePresent = FALSE; + hr = S_OK; + } + else + { + NativeAssert::Succeeded(hr, "Failed to get string value from result row"); + value.fNullablePresent = TRUE; + } + + if (pExpectedValue->fSchemaV2) + { + value.fSchemaV2 = TRUE; + hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra); + NativeAssert::Succeeded(hr, "Failed to get extra string value from result row"); + } + + AssertStructValuesSame(pExpectedValue, &value); + + ReleaseNullMem(value.pbBinary); + ReleaseNullStr(value.scz); + } + + void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults) + { + HRESULT hr = S_OK; + SCE_ROW_HANDLE sceRow = NULL; + + for (DWORD i = 0; i < cExpectedValues; ++i) + { + hr = SceGetNextResultRow(queryResults, &sceRow); + NativeAssert::Succeeded(hr, "Failed to get next result row"); + + VerifyRow(rgExpectedValues[i], sceRow); + ReleaseNullSceRow(sceRow); + } + + // No more results + NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow))); + } + + void TestIndex(SCE_DATABASE *pDatabase) + { + HRESULT hr = S_OK; + BYTE binary1[50] = { 0x80, 0x70 }; + BYTE binary2[40] = { 0x90, 0xAB }; + BYTE binary3[40] = { 0x85, 0x88 }; + DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888; + TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {}; + SCE_QUERY_HANDLE query = NULL; + SCE_QUERY_RESULTS_HANDLE results = NULL; + + SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL); + SetStructValues(&value2, static_cast(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL); + SetStructValues(&value3, static_cast(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL); + SetStructValues(&value4, static_cast(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL); + SetStructValues(&value5, static_cast(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL); + + // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards + InsertRow(pDatabase, &value1, TRUE); + + InsertRow(pDatabase, &value1, FALSE); + InsertRow(pDatabase, &value2, FALSE); + InsertRow(pDatabase, &value3, FALSE); + InsertRow(pDatabase, &value4, FALSE); + InsertRow(pDatabase, &value5, FALSE); + + NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey); + + // Test setting 1 column + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 }; + VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns, third column is unspecified so results are sorted by it + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery2[] = { &value5, &value2 }; + VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns, third column of index is unspecified so results are sorted by it + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery3[] = { &value5, &value2 }; + VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by + hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery4[] = { &value2, &value5 }; + VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results); + ReleaseNullSceQueryResults(results); + } + + void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase) + { + HRESULT hr = S_OK; + BYTE binary1[40] = { 0x55, 0x44 }; + DWORD dwValue1 = 58; + TableARowValue value1 = {}; + SCE_QUERY_HANDLE query = NULL; + SCE_ROW_HANDLE row = NULL; + + SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring"); + + InsertRow(pDatabase, &value1, FALSE); + + // Test setting 1 column + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 5); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryExact(&query, &row); + NativeAssert::Succeeded(hr, "Failed to run query exact"); + + VerifyRow(&value1, row); + } + + [Fact] + void SceUtilTest() + { + HRESULT hr = S_OK; + BOOL fComInitialized = FALSE; + LPWSTR sczDbPath = NULL; + SCE_DATABASE *pDatabase = NULL; + SCE_DATABASE_SCHEMA schema1 = {}; + SCE_DATABASE_SCHEMA schema2 = {}; + + try + { + hr = ::CoInitialize(0); + NativeAssert::Succeeded(hr, "Failed to initialize COM"); + fComInitialized = TRUE; + + SetupSchema(&schema1, FALSE); + SetupSchema(&schema2, TRUE); + + hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to test database"); + + FileEnsureDelete(sczDbPath); + + hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase); + NativeAssert::Succeeded(hr, "Failed to ensure database schema"); + + TestIndex(pDatabase); + + hr = SceCloseDatabase(pDatabase); + pDatabase = NULL; + NativeAssert::Succeeded(hr, "Failed to close database"); + + // Add column to schema + hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase); + NativeAssert::Succeeded(hr, "Failed to ensure database schema"); + + TestReadWriteSchemaV2(pDatabase); + } + finally + { + ReleaseSceSchema(&schema1); + ReleaseSceSchema(&schema2); + + if (NULL != pDatabase) + { + hr = SceCloseDatabase(pDatabase); + NativeAssert::Succeeded(hr, "Failed to close database"); + } + ReleaseStr(sczDbPath); + + if (fComInitialized) + { + ::CoUninitialize(); + } + } + } + }; +} diff --git a/src/test/DUtilUnitTest/StrUtilTest.cpp b/src/test/DUtilUnitTest/StrUtilTest.cpp new file mode 100644 index 00000000..406f2f23 --- /dev/null +++ b/src/test/DUtilUnitTest/StrUtilTest.cpp @@ -0,0 +1,184 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + public ref class StrUtil + { + public: + [Fact] + void StrUtilFormattedTest() + { + HRESULT hr = S_OK; + LPWSTR sczText = NULL; + + try + { + hr = StrAllocFormatted(&sczText, L"%hs - %ls - %u", "ansi string", L"unicode string", 1234); + NativeAssert::Succeeded(hr, "Failed to format string."); + NativeAssert::StringEqual(L"ansi string - unicode string - 1234", sczText); + + ReleaseNullStr(sczText); + + hr = StrAllocString(&sczText, L"repeat", 0); + NativeAssert::Succeeded(hr, "Failed to allocate string."); + + hr = StrAllocFormatted(&sczText, L"%ls and %ls", sczText, sczText); + NativeAssert::Succeeded(hr, "Failed to format string unto itself."); + NativeAssert::StringEqual(L"repeat and repeat", sczText); + } + finally + { + ReleaseStr(sczText); + } + } + + [Fact] + void StrUtilTrimTest() + { + TestTrim(L"", L""); + TestTrim(L"Blah", L"Blah"); + TestTrim(L"\t\t\tBlah", L"Blah"); + TestTrim(L"\t Blah ", L"Blah"); + TestTrim(L"Blah ", L"Blah"); + TestTrim(L"\t Spaces \t Between \t", L"Spaces \t Between"); + TestTrim(L" \t\t\t ", L""); + + TestTrimAnsi("", ""); + TestTrimAnsi("Blah", "Blah"); + TestTrimAnsi("\t\t\tBlah", "Blah"); + TestTrimAnsi(" Blah ", "Blah"); + TestTrimAnsi("Blah ", "Blah"); + TestTrimAnsi("\t Spaces \t Between \t", "Spaces \t Between"); + TestTrimAnsi(" \t\t\t ", ""); + } + + [Fact] + void StrUtilConvertTest() + { + char a[] = { 'a', 'b', 'C', 'd', '\0', '\0' }; + + TestStrAllocStringAnsi(a, 5, L"abCd"); + TestStrAllocStringAnsi(a, 4, L"abCd"); + TestStrAllocStringAnsi(a, 3, L"abC"); + TestStrAllocStringAnsi(a, 2, L"ab"); + TestStrAllocStringAnsi(a, 1, L"a"); + TestStrAllocStringAnsi(a, 0, L"abCd"); + + wchar_t b[] = { L'a', L'b', L'C', L'd', L'\0', L'\0' }; + + TestStrAnsiAllocString(b, 5, "abCd"); + TestStrAnsiAllocString(b, 4, "abCd"); + TestStrAnsiAllocString(b, 3, "abC"); + TestStrAnsiAllocString(b, 2, "ab"); + TestStrAnsiAllocString(b, 1, "a"); + TestStrAnsiAllocString(b, 0, "abCd"); + } + + private: + void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult) + { + HRESULT hr = S_OK; + LPWSTR sczOutput = NULL; + + try + { + hr = StrTrimWhitespace(&sczOutput, wzInput); + NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: {0}", wzInput); + + if (0 != wcscmp(wzExpectedResult, sczOutput)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Trimmed string \"%ls\", expected result \"%ls\", actual result \"%ls\"", wzInput, wzExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + return; + } + + void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult) + { + HRESULT hr = S_OK; + LPSTR sczOutput = NULL; + + try + { + hr = StrAnsiTrimWhitespace(&sczOutput, szInput); + NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: \"{0}\"", szInput); + + if (0 != strcmp(szExpectedResult, sczOutput)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Trimmed string \"%hs\", expected result \"%hs\", actual result \"%hs\"", szInput, szExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + return; + } + + void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult) + { + HRESULT hr = S_OK; + LPWSTR sczOutput = NULL; + + try + { + hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8); + NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", szSource); + + if (0 != wcscmp(sczOutput, wzExpectedResult)) + { + hr = E_FAIL; + ExitOnFailure(hr, "String doesn't match, expected result \"%ls\", actual result \"%ls\"", wzExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + return; + } + + void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult) + { + HRESULT hr = S_OK; + LPSTR sczOutput = NULL; + + try + { + hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8); + NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", wzSource); + + if (0 != strcmp(sczOutput, szExpectedResult)) + { + hr = E_FAIL; + ExitOnFailure(hr, "String doesn't match, expected result \"%hs\", actual result \"%hs\"", szExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + return; + } + }; +} diff --git a/src/test/DUtilUnitTest/UnitTest.rc b/src/test/DUtilUnitTest/UnitTest.rc new file mode 100644 index 00000000..bf68360a --- /dev/null +++ b/src/test/DUtilUnitTest/UnitTest.rc @@ -0,0 +1,7 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define VER_APP +#define VER_ORIGINAL_FILENAME "UnitTest.dll" +#define VER_INTERNAL_NAME "setup" +#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" +#include "wix.rc" diff --git a/src/test/DUtilUnitTest/UriUtilTest.cpp b/src/test/DUtilUnitTest/UriUtilTest.cpp new file mode 100644 index 00000000..220b3ff5 --- /dev/null +++ b/src/test/DUtilUnitTest/UriUtilTest.cpp @@ -0,0 +1,96 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace System::Text; +using namespace System::Collections::Generic; +using namespace Xunit; + +namespace CfgTests +{ + public ref class UriUtil + { + public: + [Fact] + void UriProtocolTest() + { + HRESULT hr = S_OK; + + LPCWSTR uri = L"https://localhost/"; + URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HTTPS://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HtTpS://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HTTP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"http://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"HtTp://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"file://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FILE://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FiLe://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FTP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + uri = L"ftp://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + uri = L"FtP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + LExit: + ; + } + }; +} diff --git a/src/test/DUtilUnitTest/VarHelpers.cpp b/src/test/DUtilUnitTest/VarHelpers.cpp new file mode 100644 index 00000000..aba69438 --- /dev/null +++ b/src/test/DUtilUnitTest/VarHelpers.cpp @@ -0,0 +1,147 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +namespace DutilTests +{ + using namespace System; + using namespace WixTest; + + void VarSetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) + { + HRESULT hr = S_OK; + + hr = VarSetString(pVariables, wzVariable, wzValue); + NativeAssert::Succeeded(hr, "Failed to set {0} to: {1}", wzVariable, wzValue); + } + + void VarSetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llValue) + { + HRESULT hr = S_OK; + + hr = VarSetNumeric(pVariables, wzVariable, llValue); + NativeAssert::Succeeded(hr, gcnew String("Failed to set {0} to: {1}"), gcnew String(wzVariable), llValue); + } + + void VarSetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwValue) + { + HRESULT hr = S_OK; + + hr = VarSetVersion(pVariables, wzVariable, qwValue); + NativeAssert::Succeeded(hr, gcnew String("Failed to set {0} to: 0x{1:X8}"), gcnew String(wzVariable), qwValue); + } + + void VarGetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + try + { + hr = VarGetString(pVariables, wzVariable, &scz); + NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable); + NativeAssert::StringEqual(wzExpectedValue, scz); + } + finally + { + ReleaseStr(scz); + } + } + + void VarGetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llExpectedValue) + { + HRESULT hr = S_OK; + LONGLONG llValue = 0; + + hr = VarGetNumeric(pVariables, wzVariable, &llValue); + NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable); + NativeAssert::Equal(llExpectedValue, llValue); + } + + void VarGetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwExpectedValue) + { + HRESULT hr = S_OK; + DWORD64 qwValue = 0; + + hr = VarGetVersion(pVariables, wzVariable, &qwValue); + NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable); + NativeAssert::Equal(qwExpectedValue, qwValue); + } + + void VarGetFormattedHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + try + { + hr = VarGetFormatted(pVariables, wzVariable, &scz); + NativeAssert::Succeeded(hr, "Failed to get formatted: {0}", wzVariable); + NativeAssert::StringEqual(wzExpectedValue, scz); + } + finally + { + ReleaseStr(scz); + } + } + + void VarFormatStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzIn, LPCWSTR wzExpectedValue) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + try + { + hr = VarFormatString(pVariables, wzIn, &scz, NULL); + NativeAssert::Succeeded(hr, "Failed to format string: '{0}'", wzIn); + NativeAssert::StringEqual(wzExpectedValue, scz); + } + finally + { + ReleaseStr(scz); + } + } + + void VarEscapeStringHelper(LPCWSTR wzIn, LPCWSTR wzExpectedValue) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + try + { + hr = VarEscapeString(wzIn, &scz); + NativeAssert::Succeeded(hr, "Failed to escape string: '{0}'", wzIn); + NativeAssert::StringEqual(wzExpectedValue, scz); + } + finally + { + ReleaseStr(scz); + } + } + + bool EvaluateConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = CondEvaluate(pVariables, wzCondition, &f); + NativeAssert::Succeeded(hr, "Failed to evaluate condition: '{0}'", wzCondition); + + return f ? true : false; + } + + bool EvaluateFailureConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = CondEvaluate(pVariables, wzCondition, &f); + if (E_INVALIDDATA != hr) + { + NativeAssert::Succeeded(hr, "Failed to evaluate condition: '{0}'", wzCondition); + } + + return E_INVALIDDATA == hr ? true : false; + } +} diff --git a/src/test/DUtilUnitTest/VarHelpers.h b/src/test/DUtilUnitTest/VarHelpers.h new file mode 100644 index 00000000..9b781ce6 --- /dev/null +++ b/src/test/DUtilUnitTest/VarHelpers.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace DutilTests +{ + +void VarSetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); +void VarSetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llValue); +void VarSetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwValue); +void VarGetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue); +void VarGetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llExpectedValue); +void VarGetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwExpectedValue); +void VarGetFormattedHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue); +void VarFormatStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzIn, LPCWSTR wzExpectedValue); +void VarEscapeStringHelper(LPCWSTR wzIn, LPCWSTR wzExpectedValue); +bool EvaluateConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition); +bool EvaluateFailureConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition); + +} diff --git a/src/test/DUtilUnitTest/VarUtilTest.cpp b/src/test/DUtilUnitTest/VarUtilTest.cpp new file mode 100644 index 00000000..206310f5 --- /dev/null +++ b/src/test/DUtilUnitTest/VarUtilTest.cpp @@ -0,0 +1,532 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#undef GetTempPath +#undef GetEnvironmentVariable + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +namespace DutilTests +{ + typedef struct _VarUtilContext + { + DWORD dw; + LPWSTR scz; + } VarUtilContext; + + void FreeValueContext(LPVOID pvContext) + { + if (pvContext) + { + MemFree(pvContext); + } + } + + public ref class VarUtil + { + public: + [NamedFact(Skip = "varutil Not Implemented Yet.")] + void VarUtilBasicTest() + { + HRESULT hr = S_OK; + VARIABLES_HANDLE pVariables = NULL; + + try + { + hr = VarCreate(&pVariables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + // set variables + VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); + VarSetNumericHelper(pVariables, L"PROP2", 2); + VarSetStringHelper(pVariables, L"PROP5", L"VAL5"); + VarSetStringHelper(pVariables, L"PROP3", L"VAL3"); + VarSetStringHelper(pVariables, L"PROP4", L"VAL4"); + VarSetStringHelper(pVariables, L"PROP6", L"VAL6"); + VarSetStringHelper(pVariables, L"PROP7", L"7"); + VarSetVersionHelper(pVariables, L"PROP8", MAKEQWORDVERSION(1, 1, 0, 0)); + + // set overwritten variables + VarSetStringHelper(pVariables, L"OVERWRITTEN_STRING", L"ORIGINAL"); + VarSetNumericHelper(pVariables, L"OVERWRITTEN_STRING", 42); + + VarSetNumericHelper(pVariables, L"OVERWRITTEN_NUMBER", 5); + VarSetStringHelper(pVariables, L"OVERWRITTEN_NUMBER", L"NEW"); + + // get and verify variable values + VarGetStringHelper(pVariables, L"PROP1", L"VAL1"); + VarGetNumericHelper(pVariables, L"PROP2", 2); + VarGetStringHelper(pVariables, L"PROP2", L"2"); + VarGetStringHelper(pVariables, L"PROP3", L"VAL3"); + VarGetStringHelper(pVariables, L"PROP4", L"VAL4"); + VarGetStringHelper(pVariables, L"PROP5", L"VAL5"); + VarGetStringHelper(pVariables, L"PROP6", L"VAL6"); + VarGetNumericHelper(pVariables, L"PROP7", 7); + VarGetVersionHelper(pVariables, L"PROP8", MAKEQWORDVERSION(1, 1, 0, 0)); + VarGetStringHelper(pVariables, L"PROP8", L"1.1.0.0"); + + VarGetNumericHelper(pVariables, L"OVERWRITTEN_STRING", 42); + VarGetStringHelper(pVariables, L"OVERWRITTEN_NUMBER", L"NEW"); + } + finally + { + ReleaseVariables(pVariables); + } + } + + [NamedFact(Skip = "varutil Not Implemented Yet.")] + void VarUtilFormatTest() + { + HRESULT hr = S_OK; + VARIABLES_HANDLE pVariables = NULL; + LPWSTR scz = NULL; + DWORD cch = 0; + try + { + hr = VarCreate(&pVariables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + // set variables + VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); + VarSetStringHelper(pVariables, L"PROP2", L"VAL2"); + VarSetNumericHelper(pVariables, L"PROP3", 3); + + // test string formatting + VarFormatStringHelper(pVariables, L"NOPROP", L"NOPROP"); + VarFormatStringHelper(pVariables, L"[PROP1]", L"VAL1"); + VarFormatStringHelper(pVariables, L" [PROP1] ", L" VAL1 "); + VarFormatStringHelper(pVariables, L"PRE [PROP1]", L"PRE VAL1"); + VarFormatStringHelper(pVariables, L"[PROP1] POST", L"VAL1 POST"); + VarFormatStringHelper(pVariables, L"PRE [PROP1] POST", L"PRE VAL1 POST"); + VarFormatStringHelper(pVariables, L"[PROP1] MID [PROP2]", L"VAL1 MID VAL2"); + VarFormatStringHelper(pVariables, L"[NONE]", L""); + VarFormatStringHelper(pVariables, L"[prop1]", L""); + VarFormatStringHelper(pVariables, L"[\\[]", L"["); + VarFormatStringHelper(pVariables, L"[\\]]", L"]"); + VarFormatStringHelper(pVariables, L"[]", L"[]"); + VarFormatStringHelper(pVariables, L"[NONE", L"[NONE"); + VarGetFormattedHelper(pVariables, L"PROP2", L"VAL2"); + VarGetFormattedHelper(pVariables, L"PROP3", L"3"); + + hr = VarFormatString(pVariables, L"PRE [PROP1] POST", &scz, &cch); + NativeAssert::Succeeded(hr, "Failed to format string."); + + Assert::Equal(lstrlenW(scz), cch); + + hr = VarFormatString(pVariables, L"PRE [PROP1] POST", NULL, &cch); + NativeAssert::Succeeded(hr, "Failed to format string."); + + Assert::Equal(lstrlenW(scz), cch); + } + finally + { + ReleaseVariables(pVariables); + ReleaseStr(scz); + } + } + + [NamedFact(Skip = "varutil Not Implemented Yet.")] + void VarUtilEscapeTest() + { + // test string escaping + VarEscapeStringHelper(L"[", L"[\\[]"); + VarEscapeStringHelper(L"]", L"[\\]]"); + VarEscapeStringHelper(L" [TEXT] ", L" [\\[]TEXT[\\]] "); + } + + [NamedFact(Skip = "varutil Not Implemented Yet.")] + void VarUtilConditionTest() + { + HRESULT hr = S_OK; + VARIABLES_HANDLE pVariables = NULL; + + try + { + hr = VarCreate(&pVariables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + // set variables + VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); + VarSetStringHelper(pVariables, L"PROP2", L"VAL2"); + VarSetStringHelper(pVariables, L"PROP3", L"VAL3"); + VarSetStringHelper(pVariables, L"PROP4", L"BEGIN MID END"); + VarSetNumericHelper(pVariables, L"PROP5", 5); + VarSetNumericHelper(pVariables, L"PROP6", 6); + VarSetStringHelper(pVariables, L"PROP7", L""); + VarSetNumericHelper(pVariables, L"PROP8", 0); + VarSetStringHelper(pVariables, L"_PROP9", L"VAL9"); + VarSetNumericHelper(pVariables, L"PROP10", -10); + VarSetNumericHelper(pVariables, L"PROP11", 9223372036854775807ll); + VarSetNumericHelper(pVariables, L"PROP12", -9223372036854775808ll); + VarSetNumericHelper(pVariables, L"PROP13", 0x00010000); + VarSetNumericHelper(pVariables, L"PROP14", 0x00000001); + VarSetNumericHelper(pVariables, L"PROP15", 0x00010001); + VarSetVersionHelper(pVariables, L"PROP16", MAKEQWORDVERSION(0, 0, 0, 0)); + VarSetVersionHelper(pVariables, L"PROP17", MAKEQWORDVERSION(1, 0, 0, 0)); + VarSetVersionHelper(pVariables, L"PROP18", MAKEQWORDVERSION(1, 1, 0, 0)); + VarSetVersionHelper(pVariables, L"PROP19", MAKEQWORDVERSION(1, 1, 1, 0)); + VarSetVersionHelper(pVariables, L"PROP20", MAKEQWORDVERSION(1, 1, 1, 1)); + VarSetNumericHelper(pVariables, L"vPROP21", 1); + VarSetVersionHelper(pVariables, L"PROP22", MAKEQWORDVERSION(65535, 65535, 65535, 65535)); + VarSetStringHelper(pVariables, L"PROP23", L"1.1.1"); + + // test conditions + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP7")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP8")); + Assert::True(EvaluateConditionHelper(pVariables, L"_PROP9")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP16")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"NONE = \"NOT\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 <> \"VAL1\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"NONE <> \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 ~= \"val1\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"val1\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 ~<> \"val1\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> \"val1\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 = 5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 = 0")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <> 5")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <> 0")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP10 = -10")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP10 <> -10")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 = v0")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 <> v1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 <> v0")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 = v0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 = v1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 = v1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP20 = v1.1.1.1")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.0")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"vPROP21 = 1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP23 = v1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 = PROP23")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> v1.1.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 <> PROP1")); + + Assert::False(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775806")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775807")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 92233720368547758070000")); + + Assert::False(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775807")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -9223372036854775809")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -92233720368547758080000")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP22 = v65535.65535.65535.65535")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65536.65535.65535.65535")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65535.655350000.65535.65535")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 < 6")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 < 5")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 > 4")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 > 5")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 6")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <= 4")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 4")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 5")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 >= 6")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 << \"BEGIN\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 << \"END\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >> \"END\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >> \"BEGIN\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >< \"MID\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >< \"NONE\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 < v1.1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP16 < v0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 > v0.12")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 > v1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP18 >= v2.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1234.1")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1.1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.0.123")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP6 = \"6\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"\"6\" = PROP6")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP6 = \"ABC\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); + Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP13 << 1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP13 << 0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP14 >> 1")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP14 >> 0")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP15 >< 65537")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP15 >< 0")); + + Assert::False(EvaluateConditionHelper(pVariables, L"NOT PROP1")); + Assert::True(EvaluateConditionHelper(pVariables, L"NOT (PROP1 <> \"VAL1\")")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); + Assert::True(EvaluateConditionHelper(pVariables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); + + Assert::True(EvaluateFailureConditionHelper(pVariables, L"=")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1 = \"")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"1A")); + Assert::True(EvaluateFailureConditionHelper(pVariables, L"*")); + + Assert::True(EvaluateFailureConditionHelper(pVariables, L"1 == 1")); + } + finally + { + ReleaseVariables(pVariables); + } + } + + [NamedFact(Skip = "varutil Not Implemented Yet.")] + void VarUtilValueTest() + { + HRESULT hr = S_OK; + VARIABLES_HANDLE pVariables = NULL; + VARIABLE_VALUE values[8]; + + try + { + hr = VarCreate(&pVariables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + // set variables + InitNumericValue(pVariables, values + 0, 2, FALSE, 1, L"PROP1"); + InitStringValue(pVariables, values + 1, L"VAL2", FALSE, 2, L"PROP2"); + InitVersionValue(pVariables, values + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, 3, L"PROP3"); + InitNoneValue(pVariables, values + 3, FALSE, 4, L"PROP4"); + InitNoneValue(pVariables, values + 4, TRUE, 5, L"PROP5"); + InitVersionValue(pVariables, values + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, 6, L"PROP6"); + InitStringValue(pVariables, values + 6, L"7", TRUE, 7, L"PROP7"); + InitNumericValue(pVariables, values + 7, 11, TRUE, 8, L"PROP8"); + + for (DWORD i = 0; i < 8; i++) + { + VerifyValue(pVariables, values + i); + } + } + finally + { + VarDestroy(pVariables, FreeValueContext); + } + } + + [NamedFact(Skip = "varutil Not Implemented Yet.")] + void VarUtilEnumTest() + { + HRESULT hr = S_OK; + const DWORD dwIndex = 8; + VARIABLES_HANDLE pVariables = NULL; + VARIABLE_ENUM_HANDLE pEnum = NULL; + VARIABLE_VALUE values[dwIndex]; + VARIABLE_VALUE* pValue = NULL; + + try + { + hr = VarCreate(&pVariables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + hr = VarStartEnum(pVariables, &pEnum, &pValue); + NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS); + + // set variables + InitNumericValue(pVariables, values + 0, 2, FALSE, 0, L"PROP1"); + InitStringValue(pVariables, values + 1, L"VAL2", FALSE, 0, L"PROP2"); + InitVersionValue(pVariables, values + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, 0, L"PROP3"); + InitNoneValue(pVariables, values + 3, FALSE, 0, L"PROP4"); + InitNoneValue(pVariables, values + 4, TRUE, 0, L"PROP5"); + InitVersionValue(pVariables, values + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, 0, L"PROP6"); + InitStringValue(pVariables, values + 6, L"7", TRUE, 0, L"PROP7"); + InitNumericValue(pVariables, values + 7, 11, TRUE, 0, L"PROP8"); + + hr = VarStartEnum(pVariables, &pEnum, &pValue); + + for (DWORD i = dwIndex - 1; i; --i) + { + NativeAssert::ValidReturnCode(hr, S_OK); + + VarUtilContext* pContext = reinterpret_cast(pValue->pvContext); + pContext->dw += 1; + + hr = VarNextVariable(pEnum, &pValue); + } + + NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS); + + for (DWORD j = 0; j < dwIndex; j++) + { + VarUtilContext* pContext = reinterpret_cast(values[j].pvContext); + NativeAssert::Equal(1, pContext->dw); + } + + VarFinishEnum(pEnum); + pEnum = NULL; + + hr = VarStartEnum(pVariables, &pEnum, &pValue); + + for (DWORD i = dwIndex - 1; i; --i) + { + NativeAssert::ValidReturnCode(hr, S_OK); + + VarUtilContext* pContext = reinterpret_cast(pValue->pvContext); + pContext->dw += 1; + + hr = VarNextVariable(pEnum, &pValue); + } + + NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS); + + for (DWORD j = 0; j < dwIndex; j++) + { + VarUtilContext* pContext = reinterpret_cast(values[j].pvContext); + NativeAssert::Equal(2, pContext->dw); + } + } + finally + { + VarFinishEnum(pEnum); + ReleaseVariableValue(pValue); + VarDestroy(pVariables, FreeValueContext); + } + } + + private: + void InitNoneValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, BOOL fHidden, DWORD dw, LPCWSTR wz) + { + pValue->type = VARIABLE_VALUE_TYPE_NONE; + pValue->fHidden = fHidden; + + InitValueContext(pValue, dw, wz); + + HRESULT hr = VarSetValue(pVariables, wz, pValue); + NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); + } + + void InitNumericValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, LONGLONG llValue, BOOL fHidden, DWORD dw, LPCWSTR wz) + { + pValue->type = VARIABLE_VALUE_TYPE_NUMERIC; + pValue->fHidden = fHidden; + + pValue->llValue = llValue; + + InitValueContext(pValue, dw, wz); + + HRESULT hr = VarSetValue(pVariables, wz, pValue); + NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); + } + + void InitStringValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, LPWSTR wzValue, BOOL fHidden, DWORD dw, LPCWSTR wz) + { + pValue->type = VARIABLE_VALUE_TYPE_STRING; + pValue->fHidden = fHidden; + + HRESULT hr = StrAllocString(&pValue->sczValue, wzValue, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); + + InitValueContext(pValue, dw, wz); + + hr = VarSetValue(pVariables, wz, pValue); + NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); + } + + void InitVersionValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, DWORD64 qwValue, BOOL fHidden, DWORD dw, LPCWSTR wz) + { + pValue->type = VARIABLE_VALUE_TYPE_VERSION; + pValue->fHidden = fHidden; + + pValue->qwValue = qwValue; + + InitValueContext(pValue, dw, wz); + + HRESULT hr = VarSetValue(pVariables, wz, pValue); + NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); + } + + void InitValueContext(VARIABLE_VALUE* pValue, DWORD dw, LPCWSTR wz) + { + pValue->pvContext = MemAlloc(sizeof(VarUtilContext), TRUE); + VarUtilContext* pContext = reinterpret_cast(pValue->pvContext); + if (!pContext) + { + throw gcnew OutOfMemoryException(); + } + + pContext->dw = dw; + + HRESULT hr = StrAllocString(&pContext->scz, wz, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wz); + } + + void VerifyValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pExpectedValue) + { + VARIABLE_VALUE* pActualValue = NULL; + + try + { + VarUtilContext* pExpectedContext = reinterpret_cast(pExpectedValue->pvContext); + NativeAssert::True(NULL != pExpectedContext); + + HRESULT hr = VarGetValue(pVariables, pExpectedContext->scz, &pActualValue); + NativeAssert::Succeeded(hr, "Failed to get value: {0}", pExpectedContext->scz); + + NativeAssert::Equal(pExpectedValue->type, pActualValue->type); + NativeAssert::InRange(pExpectedValue->type, VARIABLE_VALUE_TYPE_NONE, VARIABLE_VALUE_TYPE_STRING); + + switch (pExpectedValue->type) + { + case VARIABLE_VALUE_TYPE_NONE: + case VARIABLE_VALUE_TYPE_VERSION: + NativeAssert::Equal(pExpectedValue->qwValue, pActualValue->qwValue); + break; + case VARIABLE_VALUE_TYPE_NUMERIC: + NativeAssert::Equal(pExpectedValue->llValue, pActualValue->llValue); + break; + case VARIABLE_VALUE_TYPE_STRING: + NativeAssert::StringEqual(pExpectedValue->sczValue, pActualValue->sczValue); + break; + } + + NativeAssert::Equal(pExpectedValue->fHidden, pActualValue->fHidden); + NativeAssert::True(pExpectedValue->pvContext == pActualValue->pvContext); + } + finally + { + ReleaseVariableValue(pActualValue); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/error.h b/src/test/DUtilUnitTest/error.h new file mode 100644 index 00000000..a52db56d --- /dev/null +++ b/src/test/DUtilUnitTest/error.h @@ -0,0 +1,8 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +const int ERROR_STRING_BUFFER = 1024; + +static char szMsg[ERROR_STRING_BUFFER]; +static WCHAR wzMsg[ERROR_STRING_BUFFER]; + +#define ExitTrace(x, f, ...) { HRESULT hrTemp = x; hr = ::StringCchPrintfA(szMsg, countof(szMsg), f, __VA_ARGS__); MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrTemp, gcnew System::String(wzMsg))); } diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config new file mode 100644 index 00000000..17dcb258 --- /dev/null +++ b/src/test/DUtilUnitTest/packages.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/test/DUtilUnitTest/precomp.cpp b/src/test/DUtilUnitTest/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/test/DUtilUnitTest/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h new file mode 100644 index 00000000..aa8f7de6 --- /dev/null +++ b/src/test/DUtilUnitTest/precomp.h @@ -0,0 +1,30 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + +// Include error.h before dutil.h +#include "error.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VarHelpers.h" + +#pragma managed +#include diff --git a/src/test/DUtilUnitTest/resource.h b/src/test/DUtilUnitTest/resource.h new file mode 100644 index 00000000..bdf252f6 --- /dev/null +++ b/src/test/DUtilUnitTest/resource.h @@ -0,0 +1,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. + -- cgit v1.2.3-55-g6feb From aeddc77fc021f11f68a4c1a093eabf9776866b80 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 12 Jul 2020 11:48:31 +1000 Subject: Integrate DUtilUnitTest into latest v4. --- dutil.sln | 12 + nuget.config | 1 + src/test/DUtilUnitTest/CondUtilTest.cpp | 190 -------- src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 62 ++- .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 11 +- src/test/DUtilUnitTest/DictUtilTest.cpp | 2 +- src/test/DUtilUnitTest/DirUtilTests.cpp | 4 +- src/test/DUtilUnitTest/FileUtilTest.cpp | 2 +- src/test/DUtilUnitTest/GuidUtilTest.cpp | 2 +- src/test/DUtilUnitTest/IniUtilTest.cpp | 2 +- src/test/DUtilUnitTest/MemUtilTest.cpp | 2 +- src/test/DUtilUnitTest/MonUtilTest.cpp | 4 +- src/test/DUtilUnitTest/NativeAssert.h | 4 +- src/test/DUtilUnitTest/PathUtilTest.cpp | 2 +- src/test/DUtilUnitTest/StrUtilTest.cpp | 2 +- src/test/DUtilUnitTest/UnitTest.rc | 1 - src/test/DUtilUnitTest/VarHelpers.cpp | 147 ------ src/test/DUtilUnitTest/VarHelpers.h | 20 - src/test/DUtilUnitTest/VarUtilTest.cpp | 532 --------------------- src/test/DUtilUnitTest/packages.config | 8 +- src/test/DUtilUnitTest/precomp.h | 4 +- 21 files changed, 78 insertions(+), 936 deletions(-) delete mode 100644 src/test/DUtilUnitTest/CondUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/VarHelpers.cpp delete mode 100644 src/test/DUtilUnitTest/VarHelpers.h delete mode 100644 src/test/DUtilUnitTest/VarUtilTest.cpp diff --git a/dutil.sln b/dutil.sln index ade64266..3dafbc0c 100644 --- a/dutil.sln +++ b/dutil.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 15.0.26124.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DUtilUnitTest", "src\test\DUtilUnitTest\DUtilUnitTest.vcxproj", "{AB7EE608-E5FB-42A5-831F-0DEEEA141223}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -33,6 +35,16 @@ Global {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM64.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x64.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.Build.0 = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM64.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x64.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/nuget.config b/nuget.config index 6e1ad9b5..d5ef8952 100644 --- a/nuget.config +++ b/nuget.config @@ -2,6 +2,7 @@ + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/CondUtilTest.cpp b/src/test/DUtilUnitTest/CondUtilTest.cpp deleted file mode 100644 index c808363d..00000000 --- a/src/test/DUtilUnitTest/CondUtilTest.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -namespace DutilTests -{ - using namespace System; - using namespace Xunit; - using namespace WixTest; - - public ref class CondUtil - { - public: - [NamedFact(Skip = "condutil Not Implemented Yet.")] - void CondEvaluateTest() - { - HRESULT hr = S_OK; - VARIABLES_HANDLE pVariables = NULL; - - try - { - hr = VarCreate(&pVariables); - NativeAssert::Succeeded(hr, "Failed to initialize variables."); - - // set variables - VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); - VarSetStringHelper(pVariables, L"PROP2", L"VAL2"); - VarSetStringHelper(pVariables, L"PROP3", L"VAL3"); - VarSetStringHelper(pVariables, L"PROP4", L"BEGIN MID END"); - VarSetNumericHelper(pVariables, L"PROP5", 5); - VarSetNumericHelper(pVariables, L"PROP6", 6); - VarSetStringHelper(pVariables, L"PROP7", L""); - VarSetNumericHelper(pVariables, L"PROP8", 0); - VarSetStringHelper(pVariables, L"_PROP9", L"VAL9"); - VarSetNumericHelper(pVariables, L"PROP10", -10); - VarSetNumericHelper(pVariables, L"PROP11", 9223372036854775807ll); - VarSetNumericHelper(pVariables, L"PROP12", -9223372036854775808ll); - VarSetNumericHelper(pVariables, L"PROP13", 0x00010000); - VarSetNumericHelper(pVariables, L"PROP14", 0x00000001); - VarSetNumericHelper(pVariables, L"PROP15", 0x00010001); - VarSetVersionHelper(pVariables, L"PROP16", MAKEQWORDVERSION(0, 0, 0, 0)); - VarSetVersionHelper(pVariables, L"PROP17", MAKEQWORDVERSION(1, 0, 0, 0)); - VarSetVersionHelper(pVariables, L"PROP18", MAKEQWORDVERSION(1, 1, 0, 0)); - VarSetVersionHelper(pVariables, L"PROP19", MAKEQWORDVERSION(1, 1, 1, 0)); - VarSetVersionHelper(pVariables, L"PROP20", MAKEQWORDVERSION(1, 1, 1, 1)); - VarSetNumericHelper(pVariables, L"vPROP21", 1); - VarSetVersionHelper(pVariables, L"PROP22", MAKEQWORDVERSION(65535, 65535, 65535, 65535)); - VarSetStringHelper(pVariables, L"PROP23", L"1.1.1"); - - // test conditions - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP7")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP8")); - Assert::True(EvaluateConditionHelper(pVariables, L"_PROP9")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP16")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"NONE = \"NOT\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 <> \"VAL1\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"NONE <> \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 ~= \"val1\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"val1\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 ~<> \"val1\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> \"val1\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 = 5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 = 0")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <> 5")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <> 0")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP10 = -10")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP10 <> -10")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 = v0")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 <> v1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 <> v0")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 = v0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 = v1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 = v1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP20 = v1.1.1.1")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.0")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"vPROP21 = 1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP23 = v1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 = PROP23")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> v1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 <> PROP1")); - - Assert::False(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775806")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775807")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 9223372036854775808")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 92233720368547758070000")); - - Assert::False(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775807")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775808")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -9223372036854775809")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -92233720368547758080000")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP22 = v65535.65535.65535.65535")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65536.65535.65535.65535")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65535.655350000.65535.65535")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 < 6")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 < 5")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 > 4")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 > 5")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 6")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <= 4")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 4")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 >= 6")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 << \"BEGIN\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 << \"END\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >> \"END\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >> \"BEGIN\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >< \"MID\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >< \"NONE\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 < v1.1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP16 < v0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 > v0.12")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 > v1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP18 >= v2.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1234.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1.1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.0.123")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP6 = \"6\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"\"6\" = PROP6")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP6 = \"ABC\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); - Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP13 << 1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP13 << 0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP14 >> 1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP14 >> 0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP15 >< 65537")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP15 >< 0")); - - Assert::False(EvaluateConditionHelper(pVariables, L"NOT PROP1")); - Assert::True(EvaluateConditionHelper(pVariables, L"NOT (PROP1 <> \"VAL1\")")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); - Assert::True(EvaluateConditionHelper(pVariables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); - - Assert::True(EvaluateFailureConditionHelper(pVariables, L"=")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1 = \"")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"1A")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"*")); - - Assert::True(EvaluateFailureConditionHelper(pVariables, L"1 == 1")); - } - finally - { - ReleaseVariables(pVariables); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index 292cf28a..b023d74f 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -2,7 +2,9 @@ - + + + Debug @@ -19,17 +21,19 @@ DUtilUnitTests ManagedCProj DynamicLibrary + v142 Unicode true + v4.7.2 - + + - $(WixRoot)src\libs\dutil\inc - rpcrt4.lib;dutil.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib + ..\..\dutil\inc + rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib - @@ -38,36 +42,54 @@ + + Create + + 4564;4691 + - - - + + - - $(XunitPath)\xunit.dll + + + ..\..\..\packages\xunit.abstractions.2.0.3\lib\netstandard2.0\xunit.abstractions.dll + + + ..\..\..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll + + + ..\..\..\packages\xunit.extensibility.core.2.4.1\lib\netstandard1.1\xunit.core.dll + + + ..\..\..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.4.0.37\lib\net472\WixBuildTools.TestSupport.dll - - - {95BABD97-FBDB-453A-AF8A-FA031A07B599} - WixCppCliTestTools - - - {55CB1042-647B-4347-9876-3EA607AF8DCE} - WixTestTools - + - + + + + + 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}. + + + + + diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index a83db35d..783e18c7 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -18,9 +18,6 @@ Source Files - - Source Files - Source Files @@ -51,12 +48,6 @@ Source Files - - Source Files - - - Source Files - @@ -70,7 +61,7 @@ Header Files - + Header Files diff --git a/src/test/DUtilUnitTest/DictUtilTest.cpp b/src/test/DUtilUnitTest/DictUtilTest.cpp index fd8a5953..4e9d3907 100644 --- a/src/test/DUtilUnitTest/DictUtilTest.cpp +++ b/src/test/DUtilUnitTest/DictUtilTest.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; const DWORD numIterations = 100000; diff --git a/src/test/DUtilUnitTest/DirUtilTests.cpp b/src/test/DUtilUnitTest/DirUtilTests.cpp index a965c3d5..7643366f 100644 --- a/src/test/DUtilUnitTest/DirUtilTests.cpp +++ b/src/test/DUtilUnitTest/DirUtilTests.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; namespace DutilTests { @@ -32,7 +32,7 @@ namespace DutilTests NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid); BOOL fExists = DirExists(sczFolder, NULL); - Assert::False(fExists); + Assert::False(fExists == TRUE); hr = PathConcat(sczFolder, L"foo", &sczSubFolder); NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder); diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp index 41638bdb..9bd1d0c0 100644 --- a/src/test/DUtilUnitTest/FileUtilTest.cpp +++ b/src/test/DUtilUnitTest/FileUtilTest.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; namespace DutilTests { diff --git a/src/test/DUtilUnitTest/GuidUtilTest.cpp b/src/test/DUtilUnitTest/GuidUtilTest.cpp index d0ea9a89..a6e27a09 100644 --- a/src/test/DUtilUnitTest/GuidUtilTest.cpp +++ b/src/test/DUtilUnitTest/GuidUtilTest.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; namespace DutilTests { diff --git a/src/test/DUtilUnitTest/IniUtilTest.cpp b/src/test/DUtilUnitTest/IniUtilTest.cpp index e28f357e..2edd56ab 100644 --- a/src/test/DUtilUnitTest/IniUtilTest.cpp +++ b/src/test/DUtilUnitTest/IniUtilTest.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; typedef HRESULT (__clrcall *IniFormatParameters)( INI_HANDLE diff --git a/src/test/DUtilUnitTest/MemUtilTest.cpp b/src/test/DUtilUnitTest/MemUtilTest.cpp index 6dec9682..2621da6d 100644 --- a/src/test/DUtilUnitTest/MemUtilTest.cpp +++ b/src/test/DUtilUnitTest/MemUtilTest.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; namespace DutilTests { diff --git a/src/test/DUtilUnitTest/MonUtilTest.cpp b/src/test/DUtilUnitTest/MonUtilTest.cpp index a6ed32f1..273f2eb6 100644 --- a/src/test/DUtilUnitTest/MonUtilTest.cpp +++ b/src/test/DUtilUnitTest/MonUtilTest.cpp @@ -7,7 +7,7 @@ using namespace System; using namespace System::Collections::Generic; using namespace System::Runtime::InteropServices; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; namespace DutilTests { @@ -423,7 +423,7 @@ namespace DutilTests } } - [Fact] + [Fact(Skip = "Test demonstrates failure")] void MonUtilTest() { HRESULT hr = S_OK; diff --git a/src/test/DUtilUnitTest/NativeAssert.h b/src/test/DUtilUnitTest/NativeAssert.h index b10910c0..34af4f34 100644 --- a/src/test/DUtilUnitTest/NativeAssert.h +++ b/src/test/DUtilUnitTest/NativeAssert.h @@ -2,7 +2,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -namespace WixTest { +namespace WixBuildTools { +namespace TestSupport { using namespace System; using namespace System::Collections::Generic; @@ -81,3 +82,4 @@ namespace WixTest { } }; } +} diff --git a/src/test/DUtilUnitTest/PathUtilTest.cpp b/src/test/DUtilUnitTest/PathUtilTest.cpp index 13ec3be3..5a1f06fd 100644 --- a/src/test/DUtilUnitTest/PathUtilTest.cpp +++ b/src/test/DUtilUnitTest/PathUtilTest.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; namespace DutilTests { diff --git a/src/test/DUtilUnitTest/StrUtilTest.cpp b/src/test/DUtilUnitTest/StrUtilTest.cpp index 406f2f23..7c35b7c0 100644 --- a/src/test/DUtilUnitTest/StrUtilTest.cpp +++ b/src/test/DUtilUnitTest/StrUtilTest.cpp @@ -4,7 +4,7 @@ using namespace System; using namespace Xunit; -using namespace WixTest; +using namespace WixBuildTools::TestSupport; namespace DutilTests { diff --git a/src/test/DUtilUnitTest/UnitTest.rc b/src/test/DUtilUnitTest/UnitTest.rc index bf68360a..14cebe1a 100644 --- a/src/test/DUtilUnitTest/UnitTest.rc +++ b/src/test/DUtilUnitTest/UnitTest.rc @@ -4,4 +4,3 @@ #define VER_ORIGINAL_FILENAME "UnitTest.dll" #define VER_INTERNAL_NAME "setup" #define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" -#include "wix.rc" diff --git a/src/test/DUtilUnitTest/VarHelpers.cpp b/src/test/DUtilUnitTest/VarHelpers.cpp deleted file mode 100644 index aba69438..00000000 --- a/src/test/DUtilUnitTest/VarHelpers.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -namespace DutilTests -{ - using namespace System; - using namespace WixTest; - - void VarSetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) - { - HRESULT hr = S_OK; - - hr = VarSetString(pVariables, wzVariable, wzValue); - NativeAssert::Succeeded(hr, "Failed to set {0} to: {1}", wzVariable, wzValue); - } - - void VarSetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llValue) - { - HRESULT hr = S_OK; - - hr = VarSetNumeric(pVariables, wzVariable, llValue); - NativeAssert::Succeeded(hr, gcnew String("Failed to set {0} to: {1}"), gcnew String(wzVariable), llValue); - } - - void VarSetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwValue) - { - HRESULT hr = S_OK; - - hr = VarSetVersion(pVariables, wzVariable, qwValue); - NativeAssert::Succeeded(hr, gcnew String("Failed to set {0} to: 0x{1:X8}"), gcnew String(wzVariable), qwValue); - } - - void VarGetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - - try - { - hr = VarGetString(pVariables, wzVariable, &scz); - NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable); - NativeAssert::StringEqual(wzExpectedValue, scz); - } - finally - { - ReleaseStr(scz); - } - } - - void VarGetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llExpectedValue) - { - HRESULT hr = S_OK; - LONGLONG llValue = 0; - - hr = VarGetNumeric(pVariables, wzVariable, &llValue); - NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable); - NativeAssert::Equal(llExpectedValue, llValue); - } - - void VarGetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwExpectedValue) - { - HRESULT hr = S_OK; - DWORD64 qwValue = 0; - - hr = VarGetVersion(pVariables, wzVariable, &qwValue); - NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable); - NativeAssert::Equal(qwExpectedValue, qwValue); - } - - void VarGetFormattedHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - - try - { - hr = VarGetFormatted(pVariables, wzVariable, &scz); - NativeAssert::Succeeded(hr, "Failed to get formatted: {0}", wzVariable); - NativeAssert::StringEqual(wzExpectedValue, scz); - } - finally - { - ReleaseStr(scz); - } - } - - void VarFormatStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzIn, LPCWSTR wzExpectedValue) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - - try - { - hr = VarFormatString(pVariables, wzIn, &scz, NULL); - NativeAssert::Succeeded(hr, "Failed to format string: '{0}'", wzIn); - NativeAssert::StringEqual(wzExpectedValue, scz); - } - finally - { - ReleaseStr(scz); - } - } - - void VarEscapeStringHelper(LPCWSTR wzIn, LPCWSTR wzExpectedValue) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - - try - { - hr = VarEscapeString(wzIn, &scz); - NativeAssert::Succeeded(hr, "Failed to escape string: '{0}'", wzIn); - NativeAssert::StringEqual(wzExpectedValue, scz); - } - finally - { - ReleaseStr(scz); - } - } - - bool EvaluateConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition) - { - HRESULT hr = S_OK; - BOOL f = FALSE; - - hr = CondEvaluate(pVariables, wzCondition, &f); - NativeAssert::Succeeded(hr, "Failed to evaluate condition: '{0}'", wzCondition); - - return f ? true : false; - } - - bool EvaluateFailureConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition) - { - HRESULT hr = S_OK; - BOOL f = FALSE; - - hr = CondEvaluate(pVariables, wzCondition, &f); - if (E_INVALIDDATA != hr) - { - NativeAssert::Succeeded(hr, "Failed to evaluate condition: '{0}'", wzCondition); - } - - return E_INVALIDDATA == hr ? true : false; - } -} diff --git a/src/test/DUtilUnitTest/VarHelpers.h b/src/test/DUtilUnitTest/VarHelpers.h deleted file mode 100644 index 9b781ce6..00000000 --- a/src/test/DUtilUnitTest/VarHelpers.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -namespace DutilTests -{ - -void VarSetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); -void VarSetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llValue); -void VarSetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwValue); -void VarGetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue); -void VarGetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llExpectedValue); -void VarGetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwExpectedValue); -void VarGetFormattedHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue); -void VarFormatStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzIn, LPCWSTR wzExpectedValue); -void VarEscapeStringHelper(LPCWSTR wzIn, LPCWSTR wzExpectedValue); -bool EvaluateConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition); -bool EvaluateFailureConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition); - -} diff --git a/src/test/DUtilUnitTest/VarUtilTest.cpp b/src/test/DUtilUnitTest/VarUtilTest.cpp deleted file mode 100644 index 206310f5..00000000 --- a/src/test/DUtilUnitTest/VarUtilTest.cpp +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#undef GetTempPath -#undef GetEnvironmentVariable - -using namespace System; -using namespace Xunit; -using namespace WixTest; - -namespace DutilTests -{ - typedef struct _VarUtilContext - { - DWORD dw; - LPWSTR scz; - } VarUtilContext; - - void FreeValueContext(LPVOID pvContext) - { - if (pvContext) - { - MemFree(pvContext); - } - } - - public ref class VarUtil - { - public: - [NamedFact(Skip = "varutil Not Implemented Yet.")] - void VarUtilBasicTest() - { - HRESULT hr = S_OK; - VARIABLES_HANDLE pVariables = NULL; - - try - { - hr = VarCreate(&pVariables); - NativeAssert::Succeeded(hr, "Failed to initialize variables."); - - // set variables - VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); - VarSetNumericHelper(pVariables, L"PROP2", 2); - VarSetStringHelper(pVariables, L"PROP5", L"VAL5"); - VarSetStringHelper(pVariables, L"PROP3", L"VAL3"); - VarSetStringHelper(pVariables, L"PROP4", L"VAL4"); - VarSetStringHelper(pVariables, L"PROP6", L"VAL6"); - VarSetStringHelper(pVariables, L"PROP7", L"7"); - VarSetVersionHelper(pVariables, L"PROP8", MAKEQWORDVERSION(1, 1, 0, 0)); - - // set overwritten variables - VarSetStringHelper(pVariables, L"OVERWRITTEN_STRING", L"ORIGINAL"); - VarSetNumericHelper(pVariables, L"OVERWRITTEN_STRING", 42); - - VarSetNumericHelper(pVariables, L"OVERWRITTEN_NUMBER", 5); - VarSetStringHelper(pVariables, L"OVERWRITTEN_NUMBER", L"NEW"); - - // get and verify variable values - VarGetStringHelper(pVariables, L"PROP1", L"VAL1"); - VarGetNumericHelper(pVariables, L"PROP2", 2); - VarGetStringHelper(pVariables, L"PROP2", L"2"); - VarGetStringHelper(pVariables, L"PROP3", L"VAL3"); - VarGetStringHelper(pVariables, L"PROP4", L"VAL4"); - VarGetStringHelper(pVariables, L"PROP5", L"VAL5"); - VarGetStringHelper(pVariables, L"PROP6", L"VAL6"); - VarGetNumericHelper(pVariables, L"PROP7", 7); - VarGetVersionHelper(pVariables, L"PROP8", MAKEQWORDVERSION(1, 1, 0, 0)); - VarGetStringHelper(pVariables, L"PROP8", L"1.1.0.0"); - - VarGetNumericHelper(pVariables, L"OVERWRITTEN_STRING", 42); - VarGetStringHelper(pVariables, L"OVERWRITTEN_NUMBER", L"NEW"); - } - finally - { - ReleaseVariables(pVariables); - } - } - - [NamedFact(Skip = "varutil Not Implemented Yet.")] - void VarUtilFormatTest() - { - HRESULT hr = S_OK; - VARIABLES_HANDLE pVariables = NULL; - LPWSTR scz = NULL; - DWORD cch = 0; - try - { - hr = VarCreate(&pVariables); - NativeAssert::Succeeded(hr, "Failed to initialize variables."); - - // set variables - VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); - VarSetStringHelper(pVariables, L"PROP2", L"VAL2"); - VarSetNumericHelper(pVariables, L"PROP3", 3); - - // test string formatting - VarFormatStringHelper(pVariables, L"NOPROP", L"NOPROP"); - VarFormatStringHelper(pVariables, L"[PROP1]", L"VAL1"); - VarFormatStringHelper(pVariables, L" [PROP1] ", L" VAL1 "); - VarFormatStringHelper(pVariables, L"PRE [PROP1]", L"PRE VAL1"); - VarFormatStringHelper(pVariables, L"[PROP1] POST", L"VAL1 POST"); - VarFormatStringHelper(pVariables, L"PRE [PROP1] POST", L"PRE VAL1 POST"); - VarFormatStringHelper(pVariables, L"[PROP1] MID [PROP2]", L"VAL1 MID VAL2"); - VarFormatStringHelper(pVariables, L"[NONE]", L""); - VarFormatStringHelper(pVariables, L"[prop1]", L""); - VarFormatStringHelper(pVariables, L"[\\[]", L"["); - VarFormatStringHelper(pVariables, L"[\\]]", L"]"); - VarFormatStringHelper(pVariables, L"[]", L"[]"); - VarFormatStringHelper(pVariables, L"[NONE", L"[NONE"); - VarGetFormattedHelper(pVariables, L"PROP2", L"VAL2"); - VarGetFormattedHelper(pVariables, L"PROP3", L"3"); - - hr = VarFormatString(pVariables, L"PRE [PROP1] POST", &scz, &cch); - NativeAssert::Succeeded(hr, "Failed to format string."); - - Assert::Equal(lstrlenW(scz), cch); - - hr = VarFormatString(pVariables, L"PRE [PROP1] POST", NULL, &cch); - NativeAssert::Succeeded(hr, "Failed to format string."); - - Assert::Equal(lstrlenW(scz), cch); - } - finally - { - ReleaseVariables(pVariables); - ReleaseStr(scz); - } - } - - [NamedFact(Skip = "varutil Not Implemented Yet.")] - void VarUtilEscapeTest() - { - // test string escaping - VarEscapeStringHelper(L"[", L"[\\[]"); - VarEscapeStringHelper(L"]", L"[\\]]"); - VarEscapeStringHelper(L" [TEXT] ", L" [\\[]TEXT[\\]] "); - } - - [NamedFact(Skip = "varutil Not Implemented Yet.")] - void VarUtilConditionTest() - { - HRESULT hr = S_OK; - VARIABLES_HANDLE pVariables = NULL; - - try - { - hr = VarCreate(&pVariables); - NativeAssert::Succeeded(hr, "Failed to initialize variables."); - - // set variables - VarSetStringHelper(pVariables, L"PROP1", L"VAL1"); - VarSetStringHelper(pVariables, L"PROP2", L"VAL2"); - VarSetStringHelper(pVariables, L"PROP3", L"VAL3"); - VarSetStringHelper(pVariables, L"PROP4", L"BEGIN MID END"); - VarSetNumericHelper(pVariables, L"PROP5", 5); - VarSetNumericHelper(pVariables, L"PROP6", 6); - VarSetStringHelper(pVariables, L"PROP7", L""); - VarSetNumericHelper(pVariables, L"PROP8", 0); - VarSetStringHelper(pVariables, L"_PROP9", L"VAL9"); - VarSetNumericHelper(pVariables, L"PROP10", -10); - VarSetNumericHelper(pVariables, L"PROP11", 9223372036854775807ll); - VarSetNumericHelper(pVariables, L"PROP12", -9223372036854775808ll); - VarSetNumericHelper(pVariables, L"PROP13", 0x00010000); - VarSetNumericHelper(pVariables, L"PROP14", 0x00000001); - VarSetNumericHelper(pVariables, L"PROP15", 0x00010001); - VarSetVersionHelper(pVariables, L"PROP16", MAKEQWORDVERSION(0, 0, 0, 0)); - VarSetVersionHelper(pVariables, L"PROP17", MAKEQWORDVERSION(1, 0, 0, 0)); - VarSetVersionHelper(pVariables, L"PROP18", MAKEQWORDVERSION(1, 1, 0, 0)); - VarSetVersionHelper(pVariables, L"PROP19", MAKEQWORDVERSION(1, 1, 1, 0)); - VarSetVersionHelper(pVariables, L"PROP20", MAKEQWORDVERSION(1, 1, 1, 1)); - VarSetNumericHelper(pVariables, L"vPROP21", 1); - VarSetVersionHelper(pVariables, L"PROP22", MAKEQWORDVERSION(65535, 65535, 65535, 65535)); - VarSetStringHelper(pVariables, L"PROP23", L"1.1.1"); - - // test conditions - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP7")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP8")); - Assert::True(EvaluateConditionHelper(pVariables, L"_PROP9")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP16")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"NONE = \"NOT\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 <> \"VAL1\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"NONE <> \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 ~= \"val1\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"val1\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 ~<> \"val1\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> \"val1\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 = 5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 = 0")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <> 5")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <> 0")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP10 = -10")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP10 <> -10")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 = v0")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 <> v1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 <> v0")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 = v0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 = v1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 = v1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP20 = v1.1.1.1")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.0")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"vPROP21 = 1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP23 = v1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 = PROP23")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> v1.1.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 <> PROP1")); - - Assert::False(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775806")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775807")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 9223372036854775808")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 92233720368547758070000")); - - Assert::False(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775807")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775808")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -9223372036854775809")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -92233720368547758080000")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP22 = v65535.65535.65535.65535")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65536.65535.65535.65535")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65535.655350000.65535.65535")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 < 6")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 < 5")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 > 4")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 > 5")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 6")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <= 4")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 4")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 5")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 >= 6")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 << \"BEGIN\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 << \"END\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >> \"END\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >> \"BEGIN\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >< \"MID\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >< \"NONE\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 < v1.1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP16 < v0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 > v0.12")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 > v1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP18 >= v2.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1234.1")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1.1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.0.123")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP6 = \"6\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"\"6\" = PROP6")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP6 = \"ABC\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); - Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP13 << 1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP13 << 0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP14 >> 1")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP14 >> 0")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP15 >< 65537")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP15 >< 0")); - - Assert::False(EvaluateConditionHelper(pVariables, L"NOT PROP1")); - Assert::True(EvaluateConditionHelper(pVariables, L"NOT (PROP1 <> \"VAL1\")")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); - Assert::True(EvaluateConditionHelper(pVariables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::False(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); - - Assert::True(EvaluateFailureConditionHelper(pVariables, L"=")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1 = \"")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"1A")); - Assert::True(EvaluateFailureConditionHelper(pVariables, L"*")); - - Assert::True(EvaluateFailureConditionHelper(pVariables, L"1 == 1")); - } - finally - { - ReleaseVariables(pVariables); - } - } - - [NamedFact(Skip = "varutil Not Implemented Yet.")] - void VarUtilValueTest() - { - HRESULT hr = S_OK; - VARIABLES_HANDLE pVariables = NULL; - VARIABLE_VALUE values[8]; - - try - { - hr = VarCreate(&pVariables); - NativeAssert::Succeeded(hr, "Failed to initialize variables."); - - // set variables - InitNumericValue(pVariables, values + 0, 2, FALSE, 1, L"PROP1"); - InitStringValue(pVariables, values + 1, L"VAL2", FALSE, 2, L"PROP2"); - InitVersionValue(pVariables, values + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, 3, L"PROP3"); - InitNoneValue(pVariables, values + 3, FALSE, 4, L"PROP4"); - InitNoneValue(pVariables, values + 4, TRUE, 5, L"PROP5"); - InitVersionValue(pVariables, values + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, 6, L"PROP6"); - InitStringValue(pVariables, values + 6, L"7", TRUE, 7, L"PROP7"); - InitNumericValue(pVariables, values + 7, 11, TRUE, 8, L"PROP8"); - - for (DWORD i = 0; i < 8; i++) - { - VerifyValue(pVariables, values + i); - } - } - finally - { - VarDestroy(pVariables, FreeValueContext); - } - } - - [NamedFact(Skip = "varutil Not Implemented Yet.")] - void VarUtilEnumTest() - { - HRESULT hr = S_OK; - const DWORD dwIndex = 8; - VARIABLES_HANDLE pVariables = NULL; - VARIABLE_ENUM_HANDLE pEnum = NULL; - VARIABLE_VALUE values[dwIndex]; - VARIABLE_VALUE* pValue = NULL; - - try - { - hr = VarCreate(&pVariables); - NativeAssert::Succeeded(hr, "Failed to initialize variables."); - - hr = VarStartEnum(pVariables, &pEnum, &pValue); - NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS); - - // set variables - InitNumericValue(pVariables, values + 0, 2, FALSE, 0, L"PROP1"); - InitStringValue(pVariables, values + 1, L"VAL2", FALSE, 0, L"PROP2"); - InitVersionValue(pVariables, values + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, 0, L"PROP3"); - InitNoneValue(pVariables, values + 3, FALSE, 0, L"PROP4"); - InitNoneValue(pVariables, values + 4, TRUE, 0, L"PROP5"); - InitVersionValue(pVariables, values + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, 0, L"PROP6"); - InitStringValue(pVariables, values + 6, L"7", TRUE, 0, L"PROP7"); - InitNumericValue(pVariables, values + 7, 11, TRUE, 0, L"PROP8"); - - hr = VarStartEnum(pVariables, &pEnum, &pValue); - - for (DWORD i = dwIndex - 1; i; --i) - { - NativeAssert::ValidReturnCode(hr, S_OK); - - VarUtilContext* pContext = reinterpret_cast(pValue->pvContext); - pContext->dw += 1; - - hr = VarNextVariable(pEnum, &pValue); - } - - NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS); - - for (DWORD j = 0; j < dwIndex; j++) - { - VarUtilContext* pContext = reinterpret_cast(values[j].pvContext); - NativeAssert::Equal(1, pContext->dw); - } - - VarFinishEnum(pEnum); - pEnum = NULL; - - hr = VarStartEnum(pVariables, &pEnum, &pValue); - - for (DWORD i = dwIndex - 1; i; --i) - { - NativeAssert::ValidReturnCode(hr, S_OK); - - VarUtilContext* pContext = reinterpret_cast(pValue->pvContext); - pContext->dw += 1; - - hr = VarNextVariable(pEnum, &pValue); - } - - NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS); - - for (DWORD j = 0; j < dwIndex; j++) - { - VarUtilContext* pContext = reinterpret_cast(values[j].pvContext); - NativeAssert::Equal(2, pContext->dw); - } - } - finally - { - VarFinishEnum(pEnum); - ReleaseVariableValue(pValue); - VarDestroy(pVariables, FreeValueContext); - } - } - - private: - void InitNoneValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, BOOL fHidden, DWORD dw, LPCWSTR wz) - { - pValue->type = VARIABLE_VALUE_TYPE_NONE; - pValue->fHidden = fHidden; - - InitValueContext(pValue, dw, wz); - - HRESULT hr = VarSetValue(pVariables, wz, pValue); - NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); - } - - void InitNumericValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, LONGLONG llValue, BOOL fHidden, DWORD dw, LPCWSTR wz) - { - pValue->type = VARIABLE_VALUE_TYPE_NUMERIC; - pValue->fHidden = fHidden; - - pValue->llValue = llValue; - - InitValueContext(pValue, dw, wz); - - HRESULT hr = VarSetValue(pVariables, wz, pValue); - NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); - } - - void InitStringValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, LPWSTR wzValue, BOOL fHidden, DWORD dw, LPCWSTR wz) - { - pValue->type = VARIABLE_VALUE_TYPE_STRING; - pValue->fHidden = fHidden; - - HRESULT hr = StrAllocString(&pValue->sczValue, wzValue, 0); - NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); - - InitValueContext(pValue, dw, wz); - - hr = VarSetValue(pVariables, wz, pValue); - NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); - } - - void InitVersionValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, DWORD64 qwValue, BOOL fHidden, DWORD dw, LPCWSTR wz) - { - pValue->type = VARIABLE_VALUE_TYPE_VERSION; - pValue->fHidden = fHidden; - - pValue->qwValue = qwValue; - - InitValueContext(pValue, dw, wz); - - HRESULT hr = VarSetValue(pVariables, wz, pValue); - NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz); - } - - void InitValueContext(VARIABLE_VALUE* pValue, DWORD dw, LPCWSTR wz) - { - pValue->pvContext = MemAlloc(sizeof(VarUtilContext), TRUE); - VarUtilContext* pContext = reinterpret_cast(pValue->pvContext); - if (!pContext) - { - throw gcnew OutOfMemoryException(); - } - - pContext->dw = dw; - - HRESULT hr = StrAllocString(&pContext->scz, wz, 0); - NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wz); - } - - void VerifyValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pExpectedValue) - { - VARIABLE_VALUE* pActualValue = NULL; - - try - { - VarUtilContext* pExpectedContext = reinterpret_cast(pExpectedValue->pvContext); - NativeAssert::True(NULL != pExpectedContext); - - HRESULT hr = VarGetValue(pVariables, pExpectedContext->scz, &pActualValue); - NativeAssert::Succeeded(hr, "Failed to get value: {0}", pExpectedContext->scz); - - NativeAssert::Equal(pExpectedValue->type, pActualValue->type); - NativeAssert::InRange(pExpectedValue->type, VARIABLE_VALUE_TYPE_NONE, VARIABLE_VALUE_TYPE_STRING); - - switch (pExpectedValue->type) - { - case VARIABLE_VALUE_TYPE_NONE: - case VARIABLE_VALUE_TYPE_VERSION: - NativeAssert::Equal(pExpectedValue->qwValue, pActualValue->qwValue); - break; - case VARIABLE_VALUE_TYPE_NUMERIC: - NativeAssert::Equal(pExpectedValue->llValue, pActualValue->llValue); - break; - case VARIABLE_VALUE_TYPE_STRING: - NativeAssert::StringEqual(pExpectedValue->sczValue, pActualValue->sczValue); - break; - } - - NativeAssert::Equal(pExpectedValue->fHidden, pActualValue->fHidden); - NativeAssert::True(pExpectedValue->pvContext == pActualValue->pvContext); - } - finally - { - ReleaseVariableValue(pActualValue); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config index 17dcb258..96fb423b 100644 --- a/src/test/DUtilUnitTest/packages.config +++ b/src/test/DUtilUnitTest/packages.config @@ -3,5 +3,11 @@ - + + + + + + + diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h index aa8f7de6..15dfcd1a 100644 --- a/src/test/DUtilUnitTest/precomp.h +++ b/src/test/DUtilUnitTest/precomp.h @@ -21,10 +21,8 @@ #include #include #include -#include -#include -#include "VarHelpers.h" +#include "NativeAssert.h" #pragma managed #include -- cgit v1.2.3-55-g6feb From dc558da002794cc07013e8376f3c55c73391aa0e Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 12 Jul 2020 17:07:55 +1000 Subject: Update Dutil_TraceErrorSource to filter based on the report level. --- src/dutil/dutil.cpp | 6 ++++ src/dutil/inc/dutil.h | 13 +------- src/dutil/inc/dutilsources.h | 10 +++++++ src/test/DUtilUnitTest/DUtilTests.cpp | 35 ++++++++++++++++++++++ src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 4 ++- .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 9 ++++++ src/test/DUtilUnitTest/DictUtilTest.cpp | 4 +++ src/test/DUtilUnitTest/FileUtilTest.cpp | 3 ++ src/test/DUtilUnitTest/IniUtilTest.cpp | 3 ++ src/test/DUtilUnitTest/MemUtilTest.cpp | 16 +++++++++- src/test/DUtilUnitTest/StrUtilTest.cpp | 16 +++++++--- src/test/DUtilUnitTest/UriUtilTest.cpp | 4 ++- src/test/DUtilUnitTest/error.cpp | 26 ++++++++++++++++ src/test/DUtilUnitTest/error.h | 16 ++++++---- src/test/DUtilUnitTest/precomp.h | 1 + 15 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 src/test/DUtilUnitTest/DUtilTests.cpp create mode 100644 src/test/DUtilUnitTest/error.cpp diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp index a32de516..99ce1bc6 100644 --- a/src/dutil/dutil.cpp +++ b/src/dutil/dutil.cpp @@ -408,6 +408,12 @@ DAPIV_(void) Dutil_TraceErrorSource( ... ) { + // if this is NOT an error report and we're not logging at this level, bail + if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) + { + return; + } + if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback) { va_list args; diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h index 6b57b48a..15d45d21 100644 --- a/src/dutil/inc/dutil.h +++ b/src/dutil/inc/dutil.h @@ -10,17 +10,6 @@ #define DAPIV_(type) EXTERN_C type DAPIV -// enums -typedef enum REPORT_LEVEL -{ - REPORT_NONE, // turns off report (only valid for XXXSetLevel()) - REPORT_WARNING, // written if want only warnings or reporting is on in general - REPORT_STANDARD, // written if reporting is on - REPORT_VERBOSE, // written only if verbose reporting is on - REPORT_DEBUG, // reporting useful when debugging code - REPORT_ERROR, // always gets reported, but can never be specified -} REPORT_LEVEL; - // asserts and traces typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); @@ -29,7 +18,7 @@ typedef void (CALLBACK *DUTIL_CALLBACK_TRACEERROR)( __in int iLine, __in REPORT_LEVEL rl, __in UINT source, - __in HRESULT hr, + __in HRESULT hrError, __in_z __format_string LPCSTR szFormat, __in va_list args ); diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h index bf3da16f..b03013ca 100644 --- a/src/dutil/inc/dutilsources.h +++ b/src/dutil/inc/dutilsources.h @@ -63,3 +63,13 @@ typedef enum DUTIL_SOURCE DUTIL_SOURCE_EXTERNAL = 256, } DUTIL_SOURCE; + +typedef enum REPORT_LEVEL +{ + REPORT_NONE, // turns off report (only valid for XXXSetLevel()) + REPORT_WARNING, // written if want only warnings or reporting is on in general + REPORT_STANDARD, // written if reporting is on + REPORT_VERBOSE, // written only if verbose reporting is on + REPORT_DEBUG, // reporting useful when debugging code + REPORT_ERROR, // always gets reported, but can never be specified +} REPORT_LEVEL; diff --git a/src/test/DUtilUnitTest/DUtilTests.cpp b/src/test/DUtilUnitTest/DUtilTests.cpp new file mode 100644 index 00000000..55e81d46 --- /dev/null +++ b/src/test/DUtilUnitTest/DUtilTests.cpp @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class DUtil + { + public: + [Fact] + void DUtilTraceErrorSourceFiltersOnTraceLevel() + { + DutilInitialize(&DutilTestTraceError); + + CallDutilTraceErrorSource(); + + Dutil_TraceSetLevel(REPORT_DEBUG, FALSE); + + Action^ action = gcnew Action(this, &DUtil::CallDutilTraceErrorSource); + Assert::Throws(action); + + DutilUninitialize(); + } + + private: + void CallDutilTraceErrorSource() + { + Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, DUTIL_SOURCE_EXTERNAL, E_FAIL, "Error message"); + } + }; +} diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index b023d74f..c0974780 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -36,6 +36,8 @@ + + @@ -92,4 +94,4 @@ - + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index 783e18c7..2456558f 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -24,6 +24,12 @@ Source Files + + Source Files + + + Source Files + Source Files @@ -42,6 +48,9 @@ Source Files + + Source Files + Source Files diff --git a/src/test/DUtilUnitTest/DictUtilTest.cpp b/src/test/DUtilUnitTest/DictUtilTest.cpp index 4e9d3907..4b4777d7 100644 --- a/src/test/DUtilUnitTest/DictUtilTest.cpp +++ b/src/test/DUtilUnitTest/DictUtilTest.cpp @@ -22,6 +22,8 @@ namespace DutilTests [Fact] void DictUtilTest() { + DutilInitialize(&DutilTestTraceError); + EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations); EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); @@ -29,6 +31,8 @@ namespace DutilTests StringListTestHelper(DICT_FLAG_NONE, numIterations); StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); + + DutilUninitialize(); } private: diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp index 9bd1d0c0..0087a1d5 100644 --- a/src/test/DUtilUnitTest/FileUtilTest.cpp +++ b/src/test/DUtilUnitTest/FileUtilTest.cpp @@ -18,6 +18,8 @@ namespace DutilTests LPWSTR sczTempDir = NULL; LPWSTR sczFileDir = NULL; + DutilInitialize(&DutilTestTraceError); + try { hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT); @@ -43,6 +45,7 @@ namespace DutilTests { ReleaseStr(sczTempDir); ReleaseStr(sczFileDir); + DutilUninitialize(); } } diff --git a/src/test/DUtilUnitTest/IniUtilTest.cpp b/src/test/DUtilUnitTest/IniUtilTest.cpp index 2edd56ab..946f19c5 100644 --- a/src/test/DUtilUnitTest/IniUtilTest.cpp +++ b/src/test/DUtilUnitTest/IniUtilTest.cpp @@ -24,6 +24,8 @@ namespace DutilTests 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"; 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"; + DutilInitialize(&DutilTestTraceError); + try { hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT); @@ -58,6 +60,7 @@ namespace DutilTests { ReleaseStr(sczTempIniFilePath); ReleaseStr(sczTempIniFileDir); + DutilUninitialize(); } } diff --git a/src/test/DUtilUnitTest/MemUtilTest.cpp b/src/test/DUtilUnitTest/MemUtilTest.cpp index 2621da6d..09692bfb 100644 --- a/src/test/DUtilUnitTest/MemUtilTest.cpp +++ b/src/test/DUtilUnitTest/MemUtilTest.cpp @@ -27,6 +27,8 @@ namespace DutilTests ArrayValue *rgValues = NULL; DWORD cValues = 0; + DutilInitialize(&DutilTestTraceError); + try { hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); @@ -101,7 +103,7 @@ namespace DutilTests } LExit: - return; + DutilUninitialize(); } [Fact] @@ -111,6 +113,8 @@ namespace DutilTests ArrayValue *rgValues = NULL; DWORD cValues = 0; + DutilInitialize(&DutilTestTraceError); + try { hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); @@ -180,6 +184,7 @@ namespace DutilTests finally { ReleaseMem(rgValues); + DutilUninitialize(); } } @@ -190,6 +195,8 @@ namespace DutilTests ArrayValue *rgValues = NULL; DWORD cValues = 0; + DutilInitialize(&DutilTestTraceError); + try { hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); @@ -252,6 +259,7 @@ namespace DutilTests finally { ReleaseMem(rgValues); + DutilUninitialize(); } } @@ -262,6 +270,8 @@ namespace DutilTests ArrayValue *rgValues = NULL; DWORD cValues = 0; + DutilInitialize(&DutilTestTraceError); + try { hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); @@ -326,6 +336,7 @@ namespace DutilTests finally { ReleaseMem(rgValues); + DutilUninitialize(); } } @@ -336,6 +347,8 @@ namespace DutilTests ArrayValue *rgValues = NULL; DWORD cValues = 0; + DutilInitialize(&DutilTestTraceError); + try { hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); @@ -424,6 +437,7 @@ namespace DutilTests finally { ReleaseMem(rgValues); + DutilUninitialize(); } } diff --git a/src/test/DUtilUnitTest/StrUtilTest.cpp b/src/test/DUtilUnitTest/StrUtilTest.cpp index 7c35b7c0..94fee280 100644 --- a/src/test/DUtilUnitTest/StrUtilTest.cpp +++ b/src/test/DUtilUnitTest/StrUtilTest.cpp @@ -86,6 +86,8 @@ namespace DutilTests HRESULT hr = S_OK; LPWSTR sczOutput = NULL; + DutilInitialize(&DutilTestTraceError); + try { hr = StrTrimWhitespace(&sczOutput, wzInput); @@ -103,7 +105,7 @@ namespace DutilTests } LExit: - return; + DutilUninitialize(); } void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult) @@ -111,6 +113,8 @@ namespace DutilTests HRESULT hr = S_OK; LPSTR sczOutput = NULL; + DutilInitialize(&DutilTestTraceError); + try { hr = StrAnsiTrimWhitespace(&sczOutput, szInput); @@ -128,7 +132,7 @@ namespace DutilTests } LExit: - return; + DutilUninitialize(); } void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult) @@ -136,6 +140,8 @@ namespace DutilTests HRESULT hr = S_OK; LPWSTR sczOutput = NULL; + DutilInitialize(&DutilTestTraceError); + try { hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8); @@ -153,7 +159,7 @@ namespace DutilTests } LExit: - return; + DutilUninitialize(); } void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult) @@ -161,6 +167,8 @@ namespace DutilTests HRESULT hr = S_OK; LPSTR sczOutput = NULL; + DutilInitialize(&DutilTestTraceError); + try { hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8); @@ -178,7 +186,7 @@ namespace DutilTests } LExit: - return; + DutilUninitialize(); } }; } diff --git a/src/test/DUtilUnitTest/UriUtilTest.cpp b/src/test/DUtilUnitTest/UriUtilTest.cpp index 220b3ff5..b3bf87a2 100644 --- a/src/test/DUtilUnitTest/UriUtilTest.cpp +++ b/src/test/DUtilUnitTest/UriUtilTest.cpp @@ -17,6 +17,8 @@ namespace CfgTests { HRESULT hr = S_OK; + DutilInitialize(&DutilTestTraceError); + LPCWSTR uri = L"https://localhost/"; URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; hr = UriProtocol(uri, &uriProtocol); @@ -90,7 +92,7 @@ namespace CfgTests Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); LExit: - ; + DutilUninitialize(); } }; } diff --git a/src/test/DUtilUnitTest/error.cpp b/src/test/DUtilUnitTest/error.cpp new file mode 100644 index 00000000..e51971c3 --- /dev/null +++ b/src/test/DUtilUnitTest/error.cpp @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const int ERROR_STRING_BUFFER = 1024; + +static char szMsg[ERROR_STRING_BUFFER]; +static WCHAR wzMsg[ERROR_STRING_BUFFER]; + +void CALLBACK DutilTestTraceError( + __in_z LPCSTR /*szFile*/, + __in int /*iLine*/, + __in REPORT_LEVEL /*rl*/, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + if (DUTIL_SOURCE_EXTERNAL == source) + { + ::StringCchPrintfA(szMsg, countof(szMsg), szFormat, args); + MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); + throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrError, gcnew System::String(wzMsg))); + } +} diff --git a/src/test/DUtilUnitTest/error.h b/src/test/DUtilUnitTest/error.h index a52db56d..b973acaf 100644 --- a/src/test/DUtilUnitTest/error.h +++ b/src/test/DUtilUnitTest/error.h @@ -1,8 +1,14 @@ +#pragma once // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -const int ERROR_STRING_BUFFER = 1024; +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL -static char szMsg[ERROR_STRING_BUFFER]; -static WCHAR wzMsg[ERROR_STRING_BUFFER]; - -#define ExitTrace(x, f, ...) { HRESULT hrTemp = x; hr = ::StringCchPrintfA(szMsg, countof(szMsg), f, __VA_ARGS__); MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrTemp, gcnew System::String(wzMsg))); } +void CALLBACK DutilTestTraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h index 15dfcd1a..284668fe 100644 --- a/src/test/DUtilUnitTest/precomp.h +++ b/src/test/DUtilUnitTest/precomp.h @@ -7,6 +7,7 @@ #include // Include error.h before dutil.h +#include #include "error.h" #include -- cgit v1.2.3-55-g6feb From e3940e85379b44ab8f68d01cbb6b11db8b0bad77 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 15 Jul 2020 21:31:47 +1000 Subject: Update DUtilUnitTest to reference WixBuildTools.TestSupport.Native. --- src/dutil/dutil.vcxproj | 4 +- src/dutil/packages.config | 2 +- src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 32 +++----- .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 3 - src/test/DUtilUnitTest/NativeAssert.h | 85 ---------------------- src/test/DUtilUnitTest/packages.config | 9 +-- src/test/DUtilUnitTest/precomp.h | 2 - 7 files changed, 16 insertions(+), 121 deletions(-) delete mode 100644 src/test/DUtilUnitTest/NativeAssert.h diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 4bae04d6..4dbd2e6b 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -51,7 +51,7 @@ - + @@ -196,6 +196,6 @@ 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}. - + \ No newline at end of file diff --git a/src/dutil/packages.config b/src/dutil/packages.config index 764eba29..29fbf9e4 100644 --- a/src/dutil/packages.config +++ b/src/dutil/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index c0974780..4c660aa9 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -3,8 +3,7 @@ - - + Debug @@ -21,10 +20,8 @@ DUtilUnitTests ManagedCProj DynamicLibrary - v142 Unicode true - v4.7.2 @@ -46,7 +43,7 @@ Create - + 4564;4691 @@ -56,7 +53,6 @@ - @@ -65,33 +61,23 @@ - - ..\..\..\packages\xunit.abstractions.2.0.3\lib\netstandard2.0\xunit.abstractions.dll - - - ..\..\..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll - - - ..\..\..\packages\xunit.extensibility.core.2.4.1\lib\netstandard1.1\xunit.core.dll - - - ..\..\..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll - - ..\..\..\packages\WixBuildTools.TestSupport.4.0.37\lib\net472\WixBuildTools.TestSupport.dll + ..\..\..\packages\WixBuildTools.TestSupport.4.0.40\lib\net472\WixBuildTools.TestSupport.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.40\lib\net472\WixBuildTools.TestSupport.Native.dll - + 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}. - - - + + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index 2456558f..0c83e3fa 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -70,8 +70,5 @@ Header Files - - Header Files - \ No newline at end of file diff --git a/src/test/DUtilUnitTest/NativeAssert.h b/src/test/DUtilUnitTest/NativeAssert.h deleted file mode 100644 index 34af4f34..00000000 --- a/src/test/DUtilUnitTest/NativeAssert.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -namespace WixBuildTools { -namespace TestSupport { - - using namespace System; - using namespace System::Collections::Generic; - using namespace System::Linq; - using namespace Xunit; - - public ref class NativeAssert : WixAssert - { - public: - static void NotNull(LPCWSTR wz) - { - if (!wz) - { - Assert::NotNull(nullptr); - } - } - - // For some reason, naming these NotStringEqual methods "NotEqual" breaks Intellisense in files that call any overload of the NotEqual method. - static void NotStringEqual(LPCWSTR expected, LPCWSTR actual) - { - NativeAssert::NotStringEqual(expected, actual, FALSE); - } - - static void NotStringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) - { - IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; - Assert::NotEqual(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); - } - - // For some reason, naming these StringEqual methods "Equal" breaks Intellisense in files that call any overload of the Equal method. - static void StringEqual(LPCWSTR expected, LPCWSTR actual) - { - NativeAssert::StringEqual(expected, actual, FALSE); - } - - static void StringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) - { - IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; - Assert::Equal(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); - } - - static void Succeeded(HRESULT hr, LPCSTR zFormat, LPCSTR zArg, ... array^ zArgs) - { - array^ formatArgs = gcnew array(zArgs->Length + 1); - formatArgs[0] = NativeAssert::LPSTRToString(zArg); - for (int i = 0; i < zArgs->Length; ++i) - { - formatArgs[i + 1] = NativeAssert::LPSTRToString(zArgs[i]); - } - WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); - } - - static void Succeeded(HRESULT hr, LPCSTR zFormat, ... array^ wzArgs) - { - array^ formatArgs = gcnew array(wzArgs->Length); - for (int i = 0; i < wzArgs->Length; ++i) - { - formatArgs[i] = NativeAssert::LPWSTRToString(wzArgs[i]); - } - WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); - } - - static void ValidReturnCode(HRESULT hr, ... array^ validReturnCodes) - { - Assert::Contains(hr, (IEnumerable^)validReturnCodes); - } - - private: - static String^ LPSTRToString(LPCSTR z) - { - return z ? gcnew String(z) : nullptr; - } - static String^ LPWSTRToString(LPCWSTR wz) - { - return wz ? gcnew String(wz) : nullptr; - } - }; -} -} diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config index 96fb423b..44e5fe06 100644 --- a/src/test/DUtilUnitTest/packages.config +++ b/src/test/DUtilUnitTest/packages.config @@ -1,7 +1,5 @@ - + - - @@ -9,5 +7,6 @@ - - + + + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h index 284668fe..b3a1a9cb 100644 --- a/src/test/DUtilUnitTest/precomp.h +++ b/src/test/DUtilUnitTest/precomp.h @@ -23,7 +23,5 @@ #include #include -#include "NativeAssert.h" - #pragma managed #include -- cgit v1.2.3-55-g6feb From 89042ffee375d4e36521045405271f849ae8e951 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 30 Jul 2020 11:39:22 -0600 Subject: WIXFEAT:4863 In thmutil editboxes, save the value as a literal string and format the variable when populating it. --- src/dutil/inc/thmutil.h | 1 + src/dutil/thmutil.cpp | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index 00fa1381..11eac9c2 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -36,6 +36,7 @@ typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)( typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)( __in_z LPCWSTR wzVariable, __in_z_opt LPCWSTR wzValue, + __in BOOL fFormatted, __in_opt LPVOID pvContext ); diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 6025749e..046bdc32 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -1020,7 +1020,7 @@ DAPI_(HRESULT) ThemeShowPageEx( pSavedVariable = pPage->rgSavedVariables + v; if (pSavedVariable->wzName) { - pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, pTheme->pvVariableContext); + pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, FALSE, pTheme->pvVariableContext); } } } @@ -4128,7 +4128,7 @@ static void OnBrowseDirectory( } else if (pTheme->pfnSetStringVariable) { - hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, pTheme->pvVariableContext); + hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, FALSE, pTheme->pvVariableContext); ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); } else if (pTargetControl) @@ -4233,7 +4233,7 @@ static BOOL OnButtonClicked( case THEME_CONTROL_TYPE_RADIOBUTTON: if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId)) { - pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, pTheme->pvVariableContext); + pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, FALSE, pTheme->pvVariableContext); fRefresh = TRUE; } break; @@ -4459,6 +4459,7 @@ static HRESULT ShowControl( HRESULT hr = S_OK; DWORD iPageControl = 0; HWND hwndFocus = NULL; + LPWSTR sczFormatString = NULL; LPWSTR sczText = NULL; THEME_SAVEDVARIABLE* pSavedVariable = NULL; BOOL fHide = SW_HIDE == nCmdShow; @@ -4471,7 +4472,7 @@ static HRESULT ShowControl( hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText); ThmExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); - hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, pTheme->pvVariableContext); + hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, FALSE, pTheme->pvVariableContext); ThmExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); } @@ -4621,17 +4622,13 @@ static HRESULT ShowControl( // If this is an editbox control, // try to set its default state to the state of a matching named variable. - if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_EDITBOX == pControl->type) + if (pTheme->pfnFormatString && THEME_CONTROL_TYPE_EDITBOX == pControl->type) { - hr = pTheme->pfnGetStringVariable(pControl->sczName, &sczText, pTheme->pvVariableContext); - if (E_NOTFOUND == hr) - { - ReleaseNullStr(sczText); - } - else - { - ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczName); - } + hr = StrAllocFormatted(&sczFormatString, L"[%ls]", pControl->sczName); + ThmExitOnFailure(hr, "Failed to create format string: '%ls'", pControl->sczName); + + hr = pTheme->pfnFormatString(sczFormatString, &sczText, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to format string: '%ls'", sczFormatString); if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) { @@ -4722,6 +4719,7 @@ static HRESULT ShowControl( } LExit: + ReleaseStr(sczFormatString); ReleaseStr(sczText); return hr; -- cgit v1.2.3-55-g6feb From 5816f9e2585822f7b6140fddc405d30f2a4b381d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 30 Jul 2020 12:23:11 -0600 Subject: WIXFEAT:5843 Use MSFTEDIT_CLASS for Rich Edit controls when available. --- src/dutil/thmutil.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 046bdc32..b9335469 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -47,6 +47,7 @@ static ULONG_PTR vgdiToken = 0; static ULONG_PTR vgdiHookToken = 0; static HMODULE vhHyperlinkRegisteredModule = NULL; static HMODULE vhPanelRegisteredModule = NULL; +static HMODULE vhModuleMsftEdit = NULL; static HMODULE vhModuleRichEd = NULL; static HCURSOR vhCursorHand = NULL; @@ -454,6 +455,12 @@ LExit: DAPI_(void) ThemeUninitialize() { + if (vhModuleMsftEdit) + { + ::FreeLibrary(vhModuleMsftEdit); + vhModuleMsftEdit = NULL; + } + if (vhModuleRichEd) { ::FreeLibrary(vhModuleRichEd); @@ -5049,12 +5056,17 @@ static HRESULT LoadControls( break; case THEME_CONTROL_TYPE_RICHEDIT: - if (!vhModuleRichEd) + if (!vhModuleMsftEdit && !vhModuleRichEd) { - hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); - ThmExitOnFailure(hr, "Failed to load Rich Edit control library."); + hr = LoadSystemLibrary(L"Msftedit.dll", &vhModuleMsftEdit); + if (FAILED(hr)) + { + hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); + ThmExitOnFailure(hr, "Failed to load Rich Edit control library."); + } } - wzWindowClass = RICHEDIT_CLASSW; + + wzWindowClass = vhModuleMsftEdit ? MSFTEDIT_CLASS : RICHEDIT_CLASSW; dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY; break; -- cgit v1.2.3-55-g6feb From ebdba51558d35a295df4b36a1f55f57d86d44cf3 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 30 Jul 2020 13:11:28 -0600 Subject: WIXBUG:5250 Use the progress bar's image's 4th pixel for the right side. --- src/dutil/thmutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index b9335469..3cdc09e3 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -3799,7 +3799,7 @@ static HRESULT DrawProgressBar( } // Draw the right side of the progress bar. - ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY); + ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX + 3, nSourceY, 1, dwSourceHeight, SRCCOPY); ::SelectObject(hdcMem, hDefaultBitmap); ::DeleteDC(hdcMem); -- cgit v1.2.3-55-g6feb From 8a2fa05b14244f179d9bb127c6cb23c72d3bf2ae Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 31 Jul 2020 17:01:24 -0600 Subject: Update dpiutil.h so users don't have to include ShellScalingApi.h. --- src/dutil/inc/dpiutil.h | 21 ++++++++++++--------- src/dutil/thmutil.cpp | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h index 216d3dc6..b30e2332 100644 --- a/src/dutil/inc/dpiutil.h +++ b/src/dutil/inc/dpiutil.h @@ -41,23 +41,26 @@ typedef BOOL (APIENTRY* PFN_ADJUSTWINDOWRECTEXFORDPI)( __in DWORD dwExStyle, __in UINT dpi ); -typedef HRESULT (APIENTRY *PFN_GETDPIFORMONITOR)( - __in HMONITOR hmonitor, - __in MONITOR_DPI_TYPE dpiType, - __in UINT* dpiX, - __in UINT* dpiY - ); typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( __in HWND hwnd ); typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARE)(); -typedef HRESULT (APIENTRY* PFN_SETPROCESSDPIAWARENESS)( - __in PROCESS_DPI_AWARENESS value - ); typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARENESSCONTEXT)( __in DPI_AWARENESS_CONTEXT value ); +#ifdef DPI_ENUMS_DECLARED +typedef HRESULT(APIENTRY* PFN_GETDPIFORMONITOR)( + __in HMONITOR hmonitor, + __in MONITOR_DPI_TYPE dpiType, + __in UINT* dpiX, + __in UINT* dpiY + ); +typedef HRESULT(APIENTRY* PFN_SETPROCESSDPIAWARENESS)( + __in PROCESS_DPI_AWARENESS value + ); +#endif + void DAPI DpiuInitialize(); void DAPI DpiuUninitialize(); diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 3cdc09e3..6c9c5cd6 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -660,6 +660,7 @@ DAPI_(HRESULT) ThemeCreateParentWindow( } else { + hr = S_OK; x = CW_USEDEFAULT; y = CW_USEDEFAULT; } @@ -4683,6 +4684,8 @@ static HRESULT ShowControl( ++iPageControl; } + hr = S_OK; + Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1)); } } -- cgit v1.2.3-55-g6feb From f4fdd3cd0c797e16b8edd995c58da29aacfa0d84 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sat, 19 Sep 2020 20:58:20 -0400 Subject: Remove 32-bit ARM support. --- appveyor.cmd | 2 -- src/dutil/dutil.nuspec | 2 -- src/dutil/dutil.vcxproj | 8 -------- 3 files changed, 12 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index c54f9161..54199392 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -8,12 +8,10 @@ msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 || exit /b msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141 || exit /b msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141 || exit /b -msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v141 || exit /b msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 || exit /b msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 || exit /b msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 || exit /b -msbuild -p:Configuration=Release;Platform=ARM;PlatformToolset=v142 || exit /b msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 || exit /b msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj || exit /b diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec index 45ac64f0..3499a2d5 100644 --- a/src/dutil/dutil.nuspec +++ b/src/dutil/dutil.nuspec @@ -19,11 +19,9 @@ - - diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 4dbd2e6b..e9bbb98b 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -3,10 +3,6 @@ - - Debug - ARM - Debug ARM64 @@ -15,10 +11,6 @@ Debug Win32 - - Release - ARM - Release ARM64 -- cgit v1.2.3-55-g6feb From 281ad838c5001f988aeea06a6f06ce2cc6c0991d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Aug 2020 21:28:49 -0500 Subject: Add butil exit macros. --- src/dutil/butil.cpp | 106 ++++++++++++++++++++++---------------------------- src/dutil/inc/butil.h | 35 +++++++++++++++-- 2 files changed, 78 insertions(+), 63 deletions(-) diff --git a/src/dutil/butil.cpp b/src/dutil/butil.cpp index 243befce..e04b52e9 100644 --- a/src/dutil/butil.cpp +++ b/src/dutil/butil.cpp @@ -1,7 +1,19 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" -#include "butil.h" + +// Exit macros +#define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__) // constants // From engine/registration.h @@ -10,31 +22,20 @@ const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgrade const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; // Forward declarations. +/******************************************************************** +OpenBundleKey - Opens the bundle uninstallation key for a given bundle + +NOTE: caller is responsible for closing key +********************************************************************/ static HRESULT OpenBundleKey( - __in LPCWSTR wzBundleId, + __in_z LPCWSTR wzBundleId, __in BUNDLE_INSTALL_CONTEXT context, __inout HKEY *key); -/******************************************************************** -BundleGetBundleInfo - Queries the bundle installation metadata for a given property - -RETURNS: - E_INVALIDARG - An invalid parameter was passed to the function. - HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) - The bundle is not installed - HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) - The property is unrecognized - HRESULT_FROM_WIN32(ERROR_MORE_DATA) - A buffer is too small to hold the requested data. - E_NOTIMPL: - Tried to read a bundle attribute for a type which has not been implemented - - All other returns are unexpected returns from other dutil methods. -********************************************************************/ + extern "C" HRESULT DAPI BundleGetBundleInfo( - __in LPCWSTR wzBundleId, - __in LPCWSTR wzAttribute, + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzAttribute, __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, __inout_opt LPDWORD pcchValueBuf ) @@ -51,39 +52,39 @@ extern "C" HRESULT DAPI BundleGetBundleInfo( if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute) { - ExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); } if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) { - ExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); } // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY hr = RegGetType(hkBundle, wzAttribute, &dwType); - ExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); switch (dwType) { case REG_SZ: hr = RegReadString(hkBundle, wzAttribute, &sczValue); - ExitOnFailure(hr, "Failed to read string property."); + ButilExitOnFailure(hr, "Failed to read string property."); break; case REG_DWORD: hr = RegReadNumber(hkBundle, wzAttribute, &dwValue); - ExitOnFailure(hr, "Failed to read dword property."); + ButilExitOnFailure(hr, "Failed to read dword property."); hr = StrAllocFormatted(&sczValue, L"%d", dwValue); - ExitOnFailure(hr, "Failed to format dword property as string."); + ButilExitOnFailure(hr, "Failed to format dword property as string."); break; default: - ExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); + ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); } hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - ExitOnFailure(hr, "Failed to calculate length of string"); + ButilExitOnFailure(hr, "Failed to calculate length of string"); if (lpValueBuf) { @@ -91,11 +92,11 @@ extern "C" HRESULT DAPI BundleGetBundleInfo( if (*pcchValueBuf <= cchSource) { *pcchValueBuf = ++cchSource; - ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); + ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); } hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); *pcchValueBuf = cchSource++; } @@ -107,19 +108,8 @@ LExit: return hr; } -/******************************************************************** -BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code - -NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long. - The first 38 characters are for the GUID, and the last character is for the terminating null character. -RETURNS: - E_INVALIDARG - An invalid parameter was passed to the function. - - All other returns are unexpected returns from other dutil methods. -********************************************************************/ HRESULT DAPI BundleEnumRelatedBundle( - __in LPCWSTR wzUpgradeCode, + __in_z LPCWSTR wzUpgradeCode, __in BUNDLE_INSTALL_CONTEXT context, __inout PDWORD pdwStartIndex, __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf @@ -141,22 +131,22 @@ HRESULT DAPI BundleEnumRelatedBundle( if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex) { - ExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); } hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall); - ExitOnFailure(hr, "Failed to open bundle uninstall key path."); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++) { hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey); - ExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); + ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey); - ExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle); - ExitOnFailure(hr, "Failed to open uninstall key path."); + ButilExitOnFailure(hr, "Failed to open uninstall key path."); // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); @@ -173,7 +163,7 @@ HRESULT DAPI BundleEnumRelatedBundle( { case REG_SZ: hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue); - ExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1)) { *pdwStartIndex = dwIndex; @@ -186,7 +176,7 @@ HRESULT DAPI BundleEnumRelatedBundle( break; case REG_MULTI_SZ: hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes); - ExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); for (DWORD i = 0; i < cBundleUpgradeCodes; i++) { @@ -206,7 +196,7 @@ HRESULT DAPI BundleEnumRelatedBundle( break; default: - ExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); + ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); } @@ -215,10 +205,10 @@ HRESULT DAPI BundleEnumRelatedBundle( if (lpBundleIdBuf) { hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast(&cchUninstallSubKey)); - ExitOnFailure(hr, "Failed to calculate length of string"); + ButilExitOnFailure(hr, "Failed to calculate length of string"); hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); } break; @@ -241,13 +231,9 @@ LExit: return hr; } -/******************************************************************** -OpenBundleKey - Opens the bundle uninstallation key for a given bundle -NOTE: caller is responsible for closing key -********************************************************************/ HRESULT OpenBundleKey( - __in LPCWSTR wzBundleId, + __in_z LPCWSTR wzBundleId, __in BUNDLE_INSTALL_CONTEXT context, __inout HKEY *key) { @@ -259,10 +245,10 @@ HRESULT OpenBundleKey( LPWSTR sczKeypath = NULL; hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); - ExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); - ExitOnFailure(hr, "Failed to open bundle uninstall key path."); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); LExit: ReleaseStr(sczKeypath); diff --git a/src/dutil/inc/butil.h b/src/dutil/inc/butil.h index a42cac11..d1ec73bc 100644 --- a/src/dutil/inc/butil.h +++ b/src/dutil/inc/butil.h @@ -12,15 +12,44 @@ enum BUNDLE_INSTALL_CONTEXT BUNDLE_INSTALL_CONTEXT_USER, }; + +/******************************************************************** +BundleGetBundleInfo - Queries the bundle installation metadata for a given property + +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) + The bundle is not installed + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) + The property is unrecognized + HRESULT_FROM_WIN32(ERROR_MORE_DATA) + A buffer is too small to hold the requested data. + E_NOTIMPL: + Tried to read a bundle attribute for a type which has not been implemented + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ HRESULT DAPI BundleGetBundleInfo( - __in LPCWSTR szBundleId, // Bundle code - __in LPCWSTR szAttribute, // attribute name + __in_z LPCWSTR szBundleId, // Bundle code + __in_z LPCWSTR szAttribute, // attribute name __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired __inout_opt LPDWORD pcchValueBuf // in/out buffer character count ); +/******************************************************************** +BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code + +NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long. + The first 38 characters are for the GUID, and the last character is for the terminating null character. +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ HRESULT DAPI BundleEnumRelatedBundle( - __in LPCWSTR lpUpgradeCode, + __in_z LPCWSTR lpUpgradeCode, __in BUNDLE_INSTALL_CONTEXT context, __inout PDWORD pdwStartIndex, __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf -- cgit v1.2.3-55-g6feb From d1d31466bb9f2e887a277807d60378afef9cc57d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Aug 2020 21:29:28 -0500 Subject: WIXFEAT:6210 Parse and compare bundle versions kind of like SemVer. --- src/dutil/dutil.vcxproj | 2 + src/dutil/dutil.vcxproj.filters | 6 + src/dutil/inc/dutilsources.h | 1 + src/dutil/inc/verutil.h | 93 +++ src/dutil/precomp.h | 1 + src/dutil/verutil.cpp | 631 +++++++++++++++ src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 1 + .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 3 + src/test/DUtilUnitTest/VerUtilTests.cpp | 891 +++++++++++++++++++++ src/test/DUtilUnitTest/precomp.h | 1 + 10 files changed, 1630 insertions(+) create mode 100644 src/dutil/inc/verutil.h create mode 100644 src/dutil/verutil.cpp create mode 100644 src/test/DUtilUnitTest/VerUtilTests.cpp diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index e9bbb98b..017f7a6f 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -111,6 +111,7 @@ + @@ -167,6 +168,7 @@ + diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters index 01dd6661..b93d166b 100644 --- a/src/dutil/dutil.vcxproj.filters +++ b/src/dutil/dutil.vcxproj.filters @@ -159,6 +159,9 @@ Source Files + + Source Files + Source Files @@ -329,6 +332,9 @@ Header Files + + Header Files + Header Files diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h index b03013ca..7d512cb3 100644 --- a/src/dutil/inc/dutilsources.h +++ b/src/dutil/inc/dutilsources.h @@ -60,6 +60,7 @@ typedef enum DUTIL_SOURCE DUTIL_SOURCE_WIUTIL, DUTIL_SOURCE_WUAUTIL, DUTIL_SOURCE_XMLUTIL, + DUTIL_SOURCE_VERUTIL, DUTIL_SOURCE_EXTERNAL = 256, } DUTIL_SOURCE; diff --git a/src/dutil/inc/verutil.h b/src/dutil/inc/verutil.h new file mode 100644 index 00000000..d3715049 --- /dev/null +++ b/src/dutil/inc/verutil.h @@ -0,0 +1,93 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; } + +typedef struct _VERUTIL_VERSION_RELEASE_LABEL +{ + BOOL fNumeric; + DWORD dwValue; + DWORD_PTR cchLabelOffset; + int cchLabel; +} VERUTIL_VERSION_RELEASE_LABEL; + +typedef struct _VERUTIL_VERSION +{ + LPWSTR sczVersion; + DWORD dwMajor; + DWORD dwMinor; + DWORD dwPatch; + DWORD dwRevision; + DWORD cReleaseLabels; + VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels; + DWORD_PTR cchMetadataOffset; + BOOL fInvalid; +} VERUTIL_VERSION; + +/******************************************************************* + VerCompareParsedVersions - compares the Verutil versions. + +*******************************************************************/ +HRESULT DAPI VerCompareParsedVersions( + __in VERUTIL_VERSION* pVersion1, + __in VERUTIL_VERSION* pVersion2, + __out int* pnResult + ); + +/******************************************************************* + VerCompareStringVersions - parses the strings with VerParseVersion and then + compares the Verutil versions with VerCompareParsedVersions. + +*******************************************************************/ +HRESULT DAPI VerCompareStringVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __in BOOL fStrict, + __out int* pnResult + ); + +/******************************************************************** + VerCopyVersion - copies the given Verutil version. + +*******************************************************************/ +HRESULT DAPI VerCopyVersion( + __in VERUTIL_VERSION* pSource, + __out VERUTIL_VERSION** ppVersion + ); + +/******************************************************************** + VerFreeVersion - frees any memory associated with a Verutil version. + +*******************************************************************/ +void DAPI VerFreeVersion( + __in VERUTIL_VERSION* pVersion + ); + +/******************************************************************* + VerParseVersion - parses the string into a Verutil version. + +*******************************************************************/ +HRESULT DAPI VerParseVersion( + __in_z LPCWSTR wzVersion, + __in DWORD cchVersion, + __in BOOL fStrict, + __out VERUTIL_VERSION** ppVersion + ); + +/******************************************************************* + VerParseVersion - parses the QWORD into a Verutil version. + +*******************************************************************/ +HRESULT DAPI VerVersionFromQword( + __in DWORD64 qwVersion, + __out VERUTIL_VERSION** ppVersion + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h index 7fdc83ae..be58860c 100644 --- a/src/dutil/precomp.h +++ b/src/dutil/precomp.h @@ -89,6 +89,7 @@ #include "uncutil.h" #include "uriutil.h" #include "userutil.h" +#include "verutil.h" #include "wiutil.h" #include "wuautil.h" #include // This header is needed for msxml2.h to compile correctly diff --git a/src/dutil/verutil.cpp b/src/dutil/verutil.cpp new file mode 100644 index 00000000..f362f413 --- /dev/null +++ b/src/dutil/verutil.cpp @@ -0,0 +1,631 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) +#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) +#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) +#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) +#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__) + +// constants +const DWORD GROW_RELEASE_LABELS = 3; + +// Forward declarations. +static int CompareDword( + __in const DWORD& dw1, + __in const DWORD& dw2 + ); +static HRESULT CompareReleaseLabel( + __in const VERUTIL_VERSION_RELEASE_LABEL* p1, + __in LPCWSTR wzVersion1, + __in const VERUTIL_VERSION_RELEASE_LABEL* p2, + __in LPCWSTR wzVersion2, + __out int* pnResult + ); +static HRESULT CompareVersionSubstring( + __in LPCWSTR wzString1, + __in int cchCount1, + __in LPCWSTR wzString2, + __in int cchCount2, + __out int* pnResult + ); + + +DAPI_(HRESULT) VerCompareParsedVersions( + __in VERUTIL_VERSION* pVersion1, + __in VERUTIL_VERSION* pVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + DWORD cMaxReleaseLabels = 0; + BOOL fCompareMetadata = FALSE; + + if (!pVersion1 || !pVersion1->sczVersion || + !pVersion2 || !pVersion2->sczVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + + if (pVersion1 == pVersion2) + { + ExitFunction1(nResult = 0); + } + + nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision); + if (0 != nResult) + { + ExitFunction(); + } + + if (pVersion1->fInvalid) + { + if (!pVersion2->fInvalid) + { + ExitFunction1(nResult = -1); + } + else + { + fCompareMetadata = TRUE; + } + } + else if (pVersion2->fInvalid) + { + ExitFunction1(nResult = 1); + } + + if (pVersion1->cReleaseLabels) + { + if (pVersion2->cReleaseLabels) + { + cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels); + } + else + { + ExitFunction1(nResult = -1); + } + } + else if (pVersion2->cReleaseLabels) + { + ExitFunction1(nResult = 1); + } + + if (cMaxReleaseLabels) + { + for (DWORD i = 0; i < cMaxReleaseLabels; ++i) + { + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL; + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL; + + hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult); + if (FAILED(hr) || 0 != nResult) + { + ExitFunction(); + } + } + } + + if (fCompareMetadata) + { + hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult); + } + +LExit: + *pnResult = nResult; + return hr; +} + +DAPI_(HRESULT) VerCompareStringVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __in BOOL fStrict, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + int nResult = 0; + + hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1); + VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2); + VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2); + + hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); + VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2); + +LExit: + *pnResult = nResult; + + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + + return hr; +} + +DAPI_(HRESULT) VerCopyVersion( + __in VERUTIL_VERSION* pSource, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pCopy = NULL; + + pCopy = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy."); + + hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0); + VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion); + + pCopy->dwMajor = pSource->dwMajor; + pCopy->dwMinor = pSource->dwMinor; + pCopy->dwPatch = pSource->dwPatch; + pCopy->dwRevision = pSource->dwRevision; + + hr = MemEnsureArraySize(reinterpret_cast(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels); + VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies."); + + pCopy->cReleaseLabels = pSource->cReleaseLabels; + + for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i) + { + VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i; + VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i; + + pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset; + pCopyLabel->cchLabel = pSourceLabel->cchLabel; + pCopyLabel->fNumeric = pSourceLabel->fNumeric; + pCopyLabel->dwValue = pSourceLabel->dwValue; + } + + pCopy->cchMetadataOffset = pSource->cchMetadataOffset; + pCopy->fInvalid = pSource->fInvalid; + + *ppVersion = pCopy; + pCopy = NULL; + +LExit: + ReleaseVerutilVersion(pCopy); + + return hr; +} + +DAPI_(void) VerFreeVersion( + __in VERUTIL_VERSION* pVersion + ) +{ + if (pVersion) + { + ReleaseStr(pVersion->sczVersion); + ReleaseMem(pVersion->rgReleaseLabels); + ReleaseMem(pVersion); + } +} + +DAPI_(HRESULT) VerParseVersion( + __in_z LPCWSTR wzVersion, + __in DWORD cchVersion, + __in BOOL fStrict, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + LPCWSTR wzEnd = NULL; + LPCWSTR wzPartBegin = NULL; + LPCWSTR wzPartEnd = NULL; + BOOL fInvalid = FALSE; + BOOL fLastPart = FALSE; + BOOL fTrailingDot = FALSE; + BOOL fParsedVersionNumber = FALSE; + BOOL fExpectedReleaseLabels = FALSE; + DWORD iPart = 0; + + if (!wzVersion || !ppVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + + // Get string length if not provided. + if (0 == cchVersion) + { + cchVersion = lstrlenW(wzVersion); + } + + if (L'v' == *wzVersion || L'V' == *wzVersion) + { + ++wzVersion; + --cchVersion; + } + + pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion); + + hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion); + VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion); + + wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion; + + // Save end pointer. + wzEnd = wzVersion + cchVersion; + + // Parse version number + while (wzPartBegin < wzEnd) + { + fTrailingDot = FALSE; + + // Find end of part. + for (;;) + { + if (wzPartEnd >= wzEnd) + { + fLastPart = TRUE; + break; + } + + switch (*wzPartEnd) + { + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + ++wzPartEnd; + continue; + case L'.': + fTrailingDot = TRUE; + break; + case L'-': + case L'+': + fLastPart = TRUE; + break; + default: + fInvalid = TRUE; + break; + } + + break; + } + + if (wzPartBegin == wzPartEnd) + { + fInvalid = TRUE; + } + + if (fInvalid) + { + break; + } + + DWORD cchPart = 0; + hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); + if (FAILED(hr)) + { + fInvalid = TRUE; + break; + } + + // Parse version part. + UINT uPart = 0; + hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart); + if (FAILED(hr)) + { + fInvalid = TRUE; + break; + } + + switch (iPart) + { + case 0: + pVersion->dwMajor = uPart; + break; + case 1: + pVersion->dwMinor = uPart; + break; + case 2: + pVersion->dwPatch = uPart; + break; + case 3: + pVersion->dwRevision = uPart; + break; + } + + if (fTrailingDot) + { + ++wzPartEnd; + } + wzPartBegin = wzPartEnd; + ++iPart; + + if (4 <= iPart || fLastPart) + { + fParsedVersionNumber = TRUE; + break; + } + } + + fInvalid |= !fParsedVersionNumber || fTrailingDot; + + if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-') + { + wzPartBegin = wzPartEnd = wzPartBegin + 1; + fExpectedReleaseLabels = TRUE; + fLastPart = FALSE; + } + + while (fExpectedReleaseLabels && wzPartBegin < wzEnd) + { + fTrailingDot = FALSE; + + // Find end of part. + for (;;) + { + if (wzPartEnd >= wzEnd) + { + fLastPart = TRUE; + break; + } + + if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' || + *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' || + *wzPartEnd >= L'a' && *wzPartEnd <= L'z' || + *wzPartEnd == L'-') + { + ++wzPartEnd; + continue; + } + else if (*wzPartEnd == L'+') + { + fLastPart = TRUE; + } + else if (*wzPartEnd == L'.') + { + fTrailingDot = TRUE; + } + else + { + fInvalid = TRUE; + } + + break; + } + + if (wzPartBegin == wzPartEnd) + { + fInvalid = TRUE; + } + + if (fInvalid) + { + break; + } + + int cchLabel = 0; + hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel); + if (FAILED(hr) || 0 > cchLabel) + { + fInvalid = TRUE; + break; + } + + hr = MemReAllocArray(reinterpret_cast(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS)); + VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion); + + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels; + ++pVersion->cReleaseLabels; + + // Try to parse as number. + UINT uLabel = 0; + hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel); + if (SUCCEEDED(hr)) + { + pReleaseLabel->fNumeric = TRUE; + pReleaseLabel->dwValue = uLabel; + } + + pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion; + pReleaseLabel->cchLabel = cchLabel; + + if (fTrailingDot) + { + ++wzPartEnd; + } + wzPartBegin = wzPartEnd; + + if (fLastPart) + { + break; + } + } + + fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot); + + if (!fInvalid && wzPartBegin < wzEnd) + { + if (*wzPartBegin == L'+') + { + wzPartBegin = wzPartEnd = wzPartBegin + 1; + } + else + { + fInvalid = TRUE; + } + } + + if (fInvalid && fStrict) + { + ExitFunction1(hr = E_INVALIDARG); + } + + pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion; + pVersion->fInvalid = fInvalid; + + *ppVersion = pVersion; + pVersion = NULL; + hr = S_OK; + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +DAPI_(HRESULT) VerVersionFromQword( + __in DWORD64 qwVersion, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD."); + + pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff); + pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff); + pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff); + pVersion->dwRevision = (WORD)(qwVersion & 0xffff); + + hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision); + ExitOnFailure(hr, "Failed to allocate and format the version string."); + + pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion); + + *ppVersion = pVersion; + pVersion = NULL; + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + + +static int CompareDword( + __in const DWORD& dw1, + __in const DWORD& dw2 + ) +{ + int nResult = 0; + + if (dw1 > dw2) + { + nResult = 1; + } + else if (dw1 < dw2) + { + nResult = -1; + } + + return nResult; +} + +static HRESULT CompareReleaseLabel( + __in const VERUTIL_VERSION_RELEASE_LABEL* p1, + __in LPCWSTR wzVersion1, + __in const VERUTIL_VERSION_RELEASE_LABEL* p2, + __in LPCWSTR wzVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + + if (p1 == p2) + { + ExitFunction(); + } + else if (p1 && !p2) + { + ExitFunction1(nResult = 1); + } + else if (!p1 && p2) + { + ExitFunction1(nResult = -1); + } + + if (p1->fNumeric) + { + if (p2->fNumeric) + { + nResult = CompareDword(p1->dwValue, p2->dwValue); + } + else + { + nResult = -1; + } + } + else + { + if (p2->fNumeric) + { + nResult = 1; + } + else + { + hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult); + } + } + +LExit: + *pnResult = nResult; + + return hr; +} + +static HRESULT CompareVersionSubstring( + __in LPCWSTR wzString1, + __in int cchCount1, + __in LPCWSTR wzString2, + __in int cchCount2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + + nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2); + if (!nResult) + { + VerExitOnLastError(hr, "Failed to compare version substrings"); + } + +LExit: + *pnResult = nResult - 2; + + return hr; +} diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index 4c660aa9..31b5a5c0 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -49,6 +49,7 @@ + diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index 0c83e3fa..fdc6d278 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -57,6 +57,9 @@ Source Files + + Source Files + diff --git a/src/test/DUtilUnitTest/VerUtilTests.cpp b/src/test/DUtilUnitTest/VerUtilTests.cpp new file mode 100644 index 00000000..58b561e9 --- /dev/null +++ b/src/test/DUtilUnitTest/VerUtilTests.cpp @@ -0,0 +1,891 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class VerUtil + { + public: + [Fact] + void VerCompareVersionsTreatsMissingRevisionAsZero() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"1.2.3.4"; + LPCWSTR wzVersion2 = L"1.2.3"; + LPCWSTR wzVersion3 = L"1.2.3.0"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(4, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(5, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(2, pVersion3->dwMinor); + Assert::Equal(3, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(7, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"1.0-2.0"; + LPCWSTR wzVersion2 = L"1.0-19"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(2, pVersion1->cReleaseLabels); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion1->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); + Assert::Equal(0, pVersion1->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); + Assert::Equal(6, pVersion1->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(1, pVersion2->cReleaseLabels); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(19, pVersion2->rgReleaseLabels[0].dwValue); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCompareVersionsHandlesNormallyInvalidVersions() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + VERUTIL_VERSION* pVersion5 = NULL; + VERUTIL_VERSION* pVersion6 = NULL; + LPCWSTR wzVersion1 = L"10.-4.0"; + LPCWSTR wzVersion2 = L"10.-2.0"; + LPCWSTR wzVersion3 = L"0"; + LPCWSTR wzVersion4 = L""; + LPCWSTR wzVersion5 = L"10-2"; + LPCWSTR wzVersion6 = L"10-4.@"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); + + hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(10, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(3, pVersion1->cchMetadataOffset); + Assert::Equal(TRUE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(10, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(3, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(0, pVersion3->dwMajor); + Assert::Equal(0, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(1, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(0, pVersion4->dwMajor); + Assert::Equal(0, pVersion4->dwMinor); + Assert::Equal(0, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(0, pVersion4->cReleaseLabels); + Assert::Equal(0, pVersion4->cchMetadataOffset); + Assert::Equal(TRUE, pVersion4->fInvalid); + + NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); + Assert::Equal(10, pVersion5->dwMajor); + Assert::Equal(0, pVersion5->dwMinor); + Assert::Equal(0, pVersion5->dwPatch); + Assert::Equal(0, pVersion5->dwRevision); + Assert::Equal(1, pVersion5->cReleaseLabels); + + Assert::Equal(TRUE, pVersion5->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion5->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion5->rgReleaseLabels[0].cchLabel); + Assert::Equal(3, pVersion5->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(4, pVersion5->cchMetadataOffset); + Assert::Equal(FALSE, pVersion5->fInvalid); + + NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); + Assert::Equal(10, pVersion6->dwMajor); + Assert::Equal(0, pVersion6->dwMinor); + Assert::Equal(0, pVersion6->dwPatch); + Assert::Equal(0, pVersion6->dwRevision); + Assert::Equal(1, pVersion6->cReleaseLabels); + + Assert::Equal(TRUE, pVersion6->rgReleaseLabels[0].fNumeric); + Assert::Equal(4, pVersion6->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion6->rgReleaseLabels[0].cchLabel); + Assert::Equal(3, pVersion6->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(5, pVersion6->cchMetadataOffset); + Assert::Equal(TRUE, pVersion6->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1); + TestVerutilCompareParsedVersions(pVersion5, pVersion6, 1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + ReleaseVerutilVersion(pVersion5); + ReleaseVerutilVersion(pVersion6); + } + } + + [Fact] + void VerCompareVersionsTreatsHyphenAsVersionSeparator() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"0.0.1-a"; + LPCWSTR wzVersion2 = L"0-2"; + LPCWSTR wzVersion3 = L"1-2"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(1, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(1, pVersion1->cReleaseLabels); + + Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(1, pVersion2->cReleaseLabels); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(3, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(0, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(1, pVersion3->cReleaseLabels); + + Assert::Equal(TRUE, pVersion3->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion3->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion3->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(3, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsIgnoresLeadingZeroes() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + LPCWSTR wzVersion1 = L"0.01-a.1"; + LPCWSTR wzVersion2 = L"0.1.0-a.1"; + LPCWSTR wzVersion3 = L"0.1-a.b.0"; + LPCWSTR wzVersion4 = L"0.1.0-a.b.000"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(1, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(2, pVersion1->cReleaseLabels); + + Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(5, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); + Assert::Equal(7, pVersion1->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(8, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(1, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(2, pVersion2->cReleaseLabels); + + Assert::Equal(FALSE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion2->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion2->rgReleaseLabels[1].cchLabel); + Assert::Equal(8, pVersion2->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(9, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(0, pVersion3->dwMajor); + Assert::Equal(1, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(3, pVersion3->cReleaseLabels); + + Assert::Equal(FALSE, pVersion3->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion3->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pVersion3->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion3->rgReleaseLabels[1].cchLabel); + Assert::Equal(6, pVersion3->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(TRUE, pVersion3->rgReleaseLabels[2].fNumeric); + Assert::Equal(0, pVersion3->rgReleaseLabels[2].dwValue); + Assert::Equal(1, pVersion3->rgReleaseLabels[2].cchLabel); + Assert::Equal(8, pVersion3->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(9, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(0, pVersion4->dwMajor); + Assert::Equal(1, pVersion4->dwMinor); + Assert::Equal(0, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(3, pVersion4->cReleaseLabels); + + Assert::Equal(FALSE, pVersion4->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion4->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion4->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pVersion4->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion4->rgReleaseLabels[1].cchLabel); + Assert::Equal(8, pVersion4->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(TRUE, pVersion4->rgReleaseLabels[2].fNumeric); + Assert::Equal(0, pVersion4->rgReleaseLabels[2].dwValue); + Assert::Equal(3, pVersion4->rgReleaseLabels[2].cchLabel); + Assert::Equal(10, pVersion4->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(13, pVersion4->cchMetadataOffset); + Assert::Equal(FALSE, pVersion4->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + } + } + + [Fact] + void VerCompareVersionsTreatsUnexpectedContentAsMetadata() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"1.2.3+abcd"; + LPCWSTR wzVersion2 = L"1.2.3.abcd"; + LPCWSTR wzVersion3 = L"1.2.3.-abcd"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(6, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(2, pVersion3->dwMinor); + Assert::Equal(3, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(6, pVersion3->cchMetadataOffset); + Assert::Equal(TRUE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1); + TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsIgnoresLeadingV() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"10.20.30.40"; + LPCWSTR wzVersion2 = L"v10.20.30.40"; + LPCWSTR wzVersion3 = L"V10.20.30.40"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(10, pVersion1->dwMajor); + Assert::Equal(20, pVersion1->dwMinor); + Assert::Equal(30, pVersion1->dwPatch); + Assert::Equal(40, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(11, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion); + Assert::Equal(10, pVersion2->dwMajor); + Assert::Equal(20, pVersion2->dwMinor); + Assert::Equal(30, pVersion2->dwPatch); + Assert::Equal(40, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(11, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion); + Assert::Equal(10, pVersion3->dwMajor); + Assert::Equal(20, pVersion3->dwMinor); + Assert::Equal(30, pVersion3->dwPatch); + Assert::Equal(40, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(11, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsHandlesTooLargeNumbers() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295"; + LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(4294967295, pVersion1->dwMajor); + Assert::Equal(4294967295, pVersion1->dwMinor); + Assert::Equal(4294967295, pVersion1->dwPatch); + Assert::Equal(4294967295, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(43, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(0, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCompareVersionsIgnoresMetadataForValidVersions() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"1.2.3+abc"; + LPCWSTR wzVersion2 = L"1.2.3+xyz"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(6, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCopyVersionCopiesVersion() + { + HRESULT hr = S_OK; + LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123"; + VERUTIL_VERSION* pSource = NULL; + VERUTIL_VERSION* pCopy = NULL; + int nResult = 0; + + try + { + hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); + NativeAssert::Succeeded(hr, "VerParseVersion failed"); + + NativeAssert::StringEqual(wzVersion, pSource->sczVersion); + Assert::Equal(1, pSource->dwMajor); + Assert::Equal(2, pSource->dwMinor); + Assert::Equal(3, pSource->dwPatch); + Assert::Equal(4, pSource->dwRevision); + Assert::Equal(5, pSource->cReleaseLabels); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[0].cchLabel); + Assert::Equal(8, pSource->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[1].cchLabel); + Assert::Equal(10, pSource->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[2].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[2].cchLabel); + Assert::Equal(12, pSource->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[3].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[3].cchLabel); + Assert::Equal(14, pSource->rgReleaseLabels[3].cchLabelOffset); + + Assert::Equal(TRUE, pSource->rgReleaseLabels[4].fNumeric); + Assert::Equal(5, pSource->rgReleaseLabels[4].dwValue); + Assert::Equal(1, pSource->rgReleaseLabels[4].cchLabel); + Assert::Equal(16, pSource->rgReleaseLabels[4].cchLabelOffset); + + Assert::Equal(18, pSource->cchMetadataOffset); + Assert::Equal(TRUE, pSource->fInvalid); + + hr = VerCopyVersion(pSource, &pCopy); + NativeAssert::Succeeded(hr, "VerCopyVersion failed"); + + Assert::False(pSource == pCopy); + Assert::False(pSource->sczVersion == pCopy->sczVersion); + Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels); + + hr = VerCompareParsedVersions(pSource, pCopy, &nResult); + NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); + + Assert::Equal(nResult, 0); + } + finally + { + ReleaseVerutilVersion(pCopy); + ReleaseVerutilVersion(pSource); + } + } + + [Fact] + void VerParseVersionTreatsTrailingDotsAsInvalid() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + VERUTIL_VERSION* pVersion5 = NULL; + VERUTIL_VERSION* pVersion6 = NULL; + VERUTIL_VERSION* pVersion7 = NULL; + LPCWSTR wzVersion1 = L"."; + LPCWSTR wzVersion2 = L"1."; + LPCWSTR wzVersion3 = L"2.1."; + LPCWSTR wzVersion4 = L"3.2.1."; + LPCWSTR wzVersion5 = L"4.3.2.1."; + LPCWSTR wzVersion6 = L"5-."; + LPCWSTR wzVersion7 = L"6-a."; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); + + hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); + + hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(0, pVersion1->cchMetadataOffset); + Assert::Equal(TRUE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(2, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(2, pVersion3->dwMajor); + Assert::Equal(1, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(4, pVersion3->cchMetadataOffset); + Assert::Equal(TRUE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(3, pVersion4->dwMajor); + Assert::Equal(2, pVersion4->dwMinor); + Assert::Equal(1, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(0, pVersion4->cReleaseLabels); + Assert::Equal(6, pVersion4->cchMetadataOffset); + Assert::Equal(TRUE, pVersion4->fInvalid); + + NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); + Assert::Equal(4, pVersion5->dwMajor); + Assert::Equal(3, pVersion5->dwMinor); + Assert::Equal(2, pVersion5->dwPatch); + Assert::Equal(1, pVersion5->dwRevision); + Assert::Equal(0, pVersion5->cReleaseLabels); + Assert::Equal(8, pVersion5->cchMetadataOffset); + Assert::Equal(TRUE, pVersion5->fInvalid); + + NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); + Assert::Equal(5, pVersion6->dwMajor); + Assert::Equal(0, pVersion6->dwMinor); + Assert::Equal(0, pVersion6->dwPatch); + Assert::Equal(0, pVersion6->dwRevision); + Assert::Equal(0, pVersion6->cReleaseLabels); + Assert::Equal(2, pVersion6->cchMetadataOffset); + Assert::Equal(TRUE, pVersion6->fInvalid); + + NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion); + Assert::Equal(6, pVersion7->dwMajor); + Assert::Equal(0, pVersion7->dwMinor); + Assert::Equal(0, pVersion7->dwPatch); + Assert::Equal(0, pVersion7->dwRevision); + Assert::Equal(1, pVersion7->cReleaseLabels); + + Assert::Equal(FALSE, pVersion7->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion7->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion7->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(4, pVersion7->cchMetadataOffset); + Assert::Equal(TRUE, pVersion7->fInvalid); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + ReleaseVerutilVersion(pVersion5); + ReleaseVerutilVersion(pVersion6); + ReleaseVerutilVersion(pVersion7); + } + } + + [Fact] + void VerVersionFromQwordCreatesVersion() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + + try + { + hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1); + NativeAssert::Succeeded(hr, "VerVersionFromQword failed"); + + NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(4, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + } + finally + { + ReleaseVerutilVersion(pVersion1); + } + } + + private: + void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult) + { + HRESULT hr = S_OK; + int nResult = 0; + + hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); + NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); + + Assert::Equal(nExpectedResult, nResult); + + hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult); + NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); + + Assert::Equal(nExpectedResult, -nResult); + } + }; +} diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h index b3a1a9cb..f665fed1 100644 --- a/src/test/DUtilUnitTest/precomp.h +++ b/src/test/DUtilUnitTest/precomp.h @@ -22,6 +22,7 @@ #include #include #include +#include #pragma managed #include -- cgit v1.2.3-55-g6feb From d3bd9187857fb42218925c4b9192b25f82e81db6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Aug 2020 21:30:21 -0500 Subject: Update apuputil and deputil to not use DWORD64 for versions. --- src/dutil/apuputil.cpp | 86 +++++++++++++++++++++++++++--------------------- src/dutil/deputil.cpp | 6 ++-- src/dutil/inc/apuputil.h | 6 ++-- src/dutil/inc/deputil.h | 2 +- src/dutil/precomp.h | 2 +- 5 files changed, 56 insertions(+), 46 deletions(-) diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp index 11aaf3f2..5bbdb8dd 100644 --- a/src/dutil/apuputil.cpp +++ b/src/dutil/apuputil.cpp @@ -22,7 +22,7 @@ static __callback int __cdecl CompareEntries( static HRESULT FilterEntries( __in APPLICATION_UPDATE_ENTRY* rgEntries, __in DWORD cEntries, - __in DWORD64 dw64CurrentVersion, + __in VERUTIL_VERSION* pCurrentVersion, __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, __inout DWORD* pcFilteredEntries ); @@ -128,7 +128,7 @@ LExit: // HRESULT DAPI ApupFilterChain( __in APPLICATION_UPDATE_CHAIN* pChain, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __out APPLICATION_UPDATE_CHAIN** ppFilteredChain ) { @@ -140,7 +140,7 @@ HRESULT DAPI ApupFilterChain( pNewChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); ExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); - hr = FilterEntries(pChain->rgEntries, pChain->cEntries, dw64Version, &prgEntries, &cEntries); + hr = FilterEntries(pChain->rgEntries, pChain->cEntries, pVersion, &prgEntries, &cEntries); ExitOnFailure(hr, "Failed to filter entries by version."); if (pChain->wzDefaultApplicationId) @@ -197,6 +197,7 @@ static HRESULT ProcessEntry( { HRESULT hr = S_OK; BOOL fVersionFound = FALSE; + int nCompareResult = 0; // First search the ATOM entry's custom elements to try and find the application update information. for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext) @@ -226,13 +227,8 @@ static HRESULT ProcessEntry( { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1)) { - DWORD dwMajor = 0; - DWORD dwMinor = 0; - - hr = FileVersionFromString(pAttribute->wzValue, &dwMajor, &dwMinor); - ExitOnFailure(hr, "Failed to parse version string from ATOM entry."); - - pApupEntry->dw64UpgradeVersion = static_cast(dwMajor) << 32 | dwMinor; + hr = VerParseVersion(pAttribute->wzValue, 0, FALSE, &pApupEntry->pUpgradeVersion); + ExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1)) { @@ -245,13 +241,9 @@ static HRESULT ProcessEntry( } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1)) { - DWORD dwMajor = 0; - DWORD dwMinor = 0; - - hr = FileVersionFromString(pElement->wzValue, &dwMajor, &dwMinor); - ExitOnFailure(hr, "Failed to parse version string from ATOM entry."); + hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion); + ExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); - pApupEntry->dw64Version = static_cast(dwMajor) << 32 | dwMinor; fVersionFound = TRUE; } } @@ -263,7 +255,10 @@ static HRESULT ProcessEntry( ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version. } - if (pApupEntry->dw64UpgradeVersion >= pApupEntry->dw64Version) + hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare version to upgrade version."); + + if (nCompareResult >= 0) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); ExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); @@ -404,31 +399,24 @@ LExit: static __callback int __cdecl CompareEntries( - void* pvContext, + void* /*pvContext*/, const void* pvLeft, const void* pvRight ) { - UNREFERENCED_PARAMETER(pvContext); - int ret = 0; const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast(pvLeft); const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast(pvRight); - if (pEntryLeft->dw64Version == pEntryRight->dw64Version) + VerCompareParsedVersions(pEntryLeft->pVersion, pEntryRight->pVersion, &ret); + if (0 == ret) { - if (pEntryLeft->dw64UpgradeVersion == pEntryRight->dw64UpgradeVersion) + VerCompareParsedVersions(pEntryLeft->pUpgradeVersion, pEntryRight->pUpgradeVersion, &ret); + if (0 == ret) { - ret = (pEntryRight->dw64TotalSize < pEntryLeft->dw64TotalSize) ? -1 : 1; + ret = (pEntryRight->dw64TotalSize < pEntryLeft->dw64TotalSize) ? -1 : + (pEntryRight->dw64TotalSize > pEntryLeft->dw64TotalSize) ? 1 : 0; } - else - { - ret = (pEntryLeft->dw64UpgradeVersion > pEntryRight->dw64UpgradeVersion) ? -1 : 1; - } - } - else - { - ret = (pEntryLeft->dw64Version > pEntryRight->dw64Version) ? -1 : 1; } return ret; @@ -438,12 +426,13 @@ static __callback int __cdecl CompareEntries( static HRESULT FilterEntries( __in APPLICATION_UPDATE_ENTRY* rgEntries, __in DWORD cEntries, - __in DWORD64 dw64CurrentVersion, + __in VERUTIL_VERSION* pCurrentVersion, __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, __inout DWORD* pcFilteredEntries ) { HRESULT hr = S_OK; + int nCompareResult = 0; size_t cbAllocSize = 0; const APPLICATION_UPDATE_ENTRY* pRequired = NULL;; LPVOID pv = NULL; @@ -453,8 +442,19 @@ static HRESULT FilterEntries( for (DWORD i = 0; i < cEntries; ++i) { const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i; - if (((pEntry->fUpgradeExclusive && dw64CurrentVersion > pEntry->dw64UpgradeVersion) || (!pEntry->fUpgradeExclusive && dw64CurrentVersion >= pEntry->dw64UpgradeVersion)) && - dw64CurrentVersion < pEntry->dw64Version) + + hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare versions."); + + if (nCompareResult >= 0) + { + continue; + } + + hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pUpgradeVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare upgrade versions."); + + if (nCompareResult > 0 || (!pEntry->fUpgradeExclusive && nCompareResult == 0)) { pRequired = pEntry; break; @@ -486,9 +486,12 @@ static HRESULT FilterEntries( hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1); ExitOnFailure(hr, "Failed to deep copy entry."); - if (pRequired->dw64Version < rgEntries[0].dw64Version) + hr = VerCompareParsedVersions(pRequired->pVersion, rgEntries[0].pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare required version."); + + if (nCompareResult < 0) { - FilterEntries(rgEntries, cEntries, pRequired->dw64Version, prgFilteredEntries, pcFilteredEntries); + FilterEntries(rgEntries, cEntries, pRequired->pVersion, prgFilteredEntries, pcFilteredEntries); } } } @@ -552,8 +555,13 @@ static HRESULT CopyEntry( } pDest->dw64TotalSize = pSrc->dw64TotalSize; - pDest->dw64UpgradeVersion = pSrc->dw64UpgradeVersion; - pDest->dw64Version = pSrc->dw64Version; + + hr = VerCopyVersion(pSrc->pUpgradeVersion, &pDest->pUpgradeVersion); + ExitOnFailure(hr, "Failed to copy upgrade version."); + + hr = VerCopyVersion(pSrc->pVersion, &pDest->pVersion); + ExitOnFailure(hr, "Failed to copy version."); + pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive; hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize); @@ -641,6 +649,8 @@ static void FreeEntry( ReleaseStr(pEntry->wzSummary); ReleaseStr(pEntry->wzContentType); ReleaseStr(pEntry->wzContent); + ReleaseVerutilVersion(pEntry->pVersion); + ReleaseVerutilVersion(pEntry->pUpgradeVersion); } } diff --git a/src/dutil/deputil.cpp b/src/dutil/deputil.cpp index b2db0dd6..d65c4348 100644 --- a/src/dutil/deputil.cpp +++ b/src/dutil/deputil.cpp @@ -33,7 +33,7 @@ DAPI_(HRESULT) DepGetProviderInformation( __in_z LPCWSTR wzProviderKey, __deref_out_z_opt LPWSTR* psczId, __deref_out_z_opt LPWSTR* psczName, - __out_opt DWORD64* pqwVersion + __deref_out_z_opt LPWSTR* psczVersion ) { HRESULT hr = S_OK; @@ -75,9 +75,9 @@ DAPI_(HRESULT) DepGetProviderInformation( } // Get the Version if requested and available. - if (pqwVersion) + if (psczVersion) { - hr = RegReadVersion(hkKey, vcszVersionValue, pqwVersion); + hr = RegReadString(hkKey, vcszVersionValue, psczVersion); if (E_FILENOTFOUND == hr) { hr = S_OK; diff --git a/src/dutil/inc/apuputil.h b/src/dutil/inc/apuputil.h index 6764bde8..15d42f5d 100644 --- a/src/dutil/inc/apuputil.h +++ b/src/dutil/inc/apuputil.h @@ -46,8 +46,8 @@ struct APPLICATION_UPDATE_ENTRY LPWSTR wzUpgradeId; BOOL fUpgradeExclusive; - DWORD64 dw64Version; - DWORD64 dw64UpgradeVersion; + VERUTIL_VERSION* pVersion; + VERUTIL_VERSION* pUpgradeVersion; DWORD64 dw64TotalSize; @@ -73,7 +73,7 @@ HRESULT DAPI ApupAllocChainFromAtom( HRESULT DAPI ApupFilterChain( __in APPLICATION_UPDATE_CHAIN* pChain, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __out APPLICATION_UPDATE_CHAIN** ppFilteredChain ); diff --git a/src/dutil/inc/deputil.h b/src/dutil/inc/deputil.h index a08d2eb5..8f5f0ae8 100644 --- a/src/dutil/inc/deputil.h +++ b/src/dutil/inc/deputil.h @@ -27,7 +27,7 @@ DAPI_(HRESULT) DepGetProviderInformation( __in_z LPCWSTR wzProviderKey, __deref_out_z_opt LPWSTR* psczId, __deref_out_z_opt LPWSTR* psczName, - __out_opt DWORD64* pqwVersion + __deref_out_z_opt LPWSTR* psczVersion ); /*************************************************************************** diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h index be58860c..f8f3b944 100644 --- a/src/dutil/precomp.h +++ b/src/dutil/precomp.h @@ -43,6 +43,7 @@ #include "dutilsources.h" #include "dutil.h" +#include "verutil.h" #include "aclutil.h" #include "atomutil.h" #include "buffutil.h" @@ -89,7 +90,6 @@ #include "uncutil.h" #include "uriutil.h" #include "userutil.h" -#include "verutil.h" #include "wiutil.h" #include "wuautil.h" #include // This header is needed for msxml2.h to compile correctly -- cgit v1.2.3-55-g6feb From c6a94a7f3556c8dc998630aa65b4e812c7898ad1 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 17 Oct 2020 19:36:11 -0500 Subject: Update verutil precedence rules to check for invalid after release labels. --- src/dutil/verutil.cpp | 57 +++++++++++++++++---------------- src/test/DUtilUnitTest/VerUtilTests.cpp | 44 ++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/dutil/verutil.cpp b/src/dutil/verutil.cpp index f362f413..835dde81 100644 --- a/src/dutil/verutil.cpp +++ b/src/dutil/verutil.cpp @@ -85,22 +85,6 @@ DAPI_(HRESULT) VerCompareParsedVersions( ExitFunction(); } - if (pVersion1->fInvalid) - { - if (!pVersion2->fInvalid) - { - ExitFunction1(nResult = -1); - } - else - { - fCompareMetadata = TRUE; - } - } - else if (pVersion2->fInvalid) - { - ExitFunction1(nResult = 1); - } - if (pVersion1->cReleaseLabels) { if (pVersion2->cReleaseLabels) @@ -132,6 +116,22 @@ DAPI_(HRESULT) VerCompareParsedVersions( } } + if (pVersion1->fInvalid) + { + if (!pVersion2->fInvalid) + { + ExitFunction1(nResult = -1); + } + else + { + fCompareMetadata = TRUE; + } + } + else if (pVersion2->fInvalid) + { + ExitFunction1(nResult = 1); + } + if (fCompareMetadata) { hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult); @@ -191,20 +191,23 @@ DAPI_(HRESULT) VerCopyVersion( pCopy->dwPatch = pSource->dwPatch; pCopy->dwRevision = pSource->dwRevision; - hr = MemEnsureArraySize(reinterpret_cast(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels); - VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies."); + if (pSource->cReleaseLabels) + { + hr = MemEnsureArraySize(reinterpret_cast(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels); + VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies."); - pCopy->cReleaseLabels = pSource->cReleaseLabels; + pCopy->cReleaseLabels = pSource->cReleaseLabels; - for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i) - { - VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i; - VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i; + for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i) + { + VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i; + VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i; - pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset; - pCopyLabel->cchLabel = pSourceLabel->cchLabel; - pCopyLabel->fNumeric = pSourceLabel->fNumeric; - pCopyLabel->dwValue = pSourceLabel->dwValue; + pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset; + pCopyLabel->cchLabel = pSourceLabel->cchLabel; + pCopyLabel->fNumeric = pSourceLabel->fNumeric; + pCopyLabel->dwValue = pSourceLabel->dwValue; + } } pCopy->cchMetadataOffset = pSource->cchMetadataOffset; diff --git a/src/test/DUtilUnitTest/VerUtilTests.cpp b/src/test/DUtilUnitTest/VerUtilTests.cpp index 58b561e9..8f24ad1a 100644 --- a/src/test/DUtilUnitTest/VerUtilTests.cpp +++ b/src/test/DUtilUnitTest/VerUtilTests.cpp @@ -237,7 +237,7 @@ namespace DutilTests TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1); - TestVerutilCompareParsedVersions(pVersion5, pVersion6, 1); + TestVerutilCompareParsedVersions(pVersion5, pVersion6, -1); } finally { @@ -661,6 +661,48 @@ namespace DutilTests [Fact] void VerCopyVersionCopiesVersion() + { + HRESULT hr = S_OK; + LPCWSTR wzVersion = L"1.2.3.4+abc123"; + VERUTIL_VERSION* pSource = NULL; + VERUTIL_VERSION* pCopy = NULL; + int nResult = 0; + + try + { + hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); + NativeAssert::Succeeded(hr, "VerParseVersion failed"); + + NativeAssert::StringEqual(wzVersion, pSource->sczVersion); + Assert::Equal(1, pSource->dwMajor); + Assert::Equal(2, pSource->dwMinor); + Assert::Equal(3, pSource->dwPatch); + Assert::Equal(4, pSource->dwRevision); + Assert::Equal(0, pSource->cReleaseLabels); + + Assert::Equal(8, pSource->cchMetadataOffset); + Assert::Equal(FALSE, pSource->fInvalid); + + hr = VerCopyVersion(pSource, &pCopy); + NativeAssert::Succeeded(hr, "VerCopyVersion failed"); + + Assert::False(pSource == pCopy); + Assert::False(pSource->sczVersion == pCopy->sczVersion); + + hr = VerCompareParsedVersions(pSource, pCopy, &nResult); + NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); + + Assert::Equal(nResult, 0); + } + finally + { + ReleaseVerutilVersion(pCopy); + ReleaseVerutilVersion(pSource); + } + } + + [Fact] + void VerCopyVersionCopiesPrereleaseVersion() { HRESULT hr = S_OK; LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123"; -- cgit v1.2.3-55-g6feb From d5925b2bd8e71933cb88a0d17298088260b5b7db Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 15 Nov 2020 12:38:41 -0600 Subject: Run unit tests in the build script. --- appveyor.cmd | 2 ++ appveyor.yml | 2 ++ src/dutil/dutil.vcxproj | 4 ++-- src/dutil/packages.config | 2 +- src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 12 ++++++------ src/test/DUtilUnitTest/packages.config | 5 +++-- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 54199392..9b6bc112 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -3,6 +3,8 @@ nuget restore || exit /b +msbuild -t:Test -p:Configuration=Release src\test\DUtilUnitTest || exit /b + msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140 || exit /b msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 || exit /b diff --git a/appveyor.yml b/appveyor.yml index 522e5af3..f602d07c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,8 @@ environment: build_script: - appveyor.cmd +test: off + pull_requests: do_not_increment_build_number: true diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 017f7a6f..2cf99d13 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -43,7 +43,7 @@ - + @@ -190,6 +190,6 @@ 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}. - + \ No newline at end of file diff --git a/src/dutil/packages.config b/src/dutil/packages.config index 29fbf9e4..1ffaa8df 100644 --- a/src/dutil/packages.config +++ b/src/dutil/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index 31b5a5c0..e1164aca 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -3,7 +3,7 @@ - + Debug @@ -63,22 +63,22 @@ - ..\..\..\packages\WixBuildTools.TestSupport.4.0.40\lib\net472\WixBuildTools.TestSupport.dll + ..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll - ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.40\lib\net472\WixBuildTools.TestSupport.Native.dll + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll - + 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}. - - + + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config index 44e5fe06..a4fef2bf 100644 --- a/src/test/DUtilUnitTest/packages.config +++ b/src/test/DUtilUnitTest/packages.config @@ -6,7 +6,8 @@ + - - + + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 6554b42e999c8ff2cf20361a7dd7ec500723ec71 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Fri, 13 Nov 2020 21:21:33 -0500 Subject: Clean up 32-bit ass-u-mptions for x64 and arm64. --- src/dutil/buffutil.cpp | 4 ++-- src/dutil/fileutil.cpp | 18 +++++++++--------- src/dutil/inc/buffutil.h | 2 +- src/dutil/inc/fileutil.h | 14 +++++++------- src/dutil/inc/strutil.h | 18 +++++++++--------- src/dutil/inc/xmlutil.h | 2 +- src/dutil/xmlutil.cpp | 4 ++-- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp index 0cc67dcb..e78db24f 100644 --- a/src/dutil/buffutil.cpp +++ b/src/dutil/buffutil.cpp @@ -257,7 +257,7 @@ LExit: extern "C" HRESULT BuffWriteNumber( __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, - __in DWORD dw + __in DWORD_PTR dw ) { Assert(ppbBuffer); @@ -270,7 +270,7 @@ extern "C" HRESULT BuffWriteNumber( ExitOnFailure(hr, "Failed to ensure buffer size."); // copy data to buffer - *(DWORD*)(*ppbBuffer + *piBuffer) = dw; + *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; *piBuffer += sizeof(DWORD); LExit: diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp index 8666da65..6191ec06 100644 --- a/src/dutil/fileutil.cpp +++ b/src/dutil/fileutil.cpp @@ -789,7 +789,7 @@ LExit: ********************************************************************/ extern "C" HRESULT DAPI FileRead( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out DWORD* pcbDest, + __out SIZE_T* pcbDest, __in LPCWSTR wzSrcPath ) { @@ -803,7 +803,7 @@ extern "C" HRESULT DAPI FileRead( ********************************************************************/ extern "C" HRESULT DAPI FileReadEx( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out DWORD* pcbDest, + __out SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in DWORD dwShareMode ) @@ -818,7 +818,7 @@ extern "C" HRESULT DAPI FileReadEx( ********************************************************************/ extern "C" HRESULT DAPI FileReadUntil( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) DWORD* pcbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in LPCWSTR wzSrcPath, __in DWORD cbMaxRead ) @@ -834,7 +834,7 @@ extern "C" HRESULT DAPI FileReadUntil( ********************************************************************/ extern "C" HRESULT DAPI FileReadPartial( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) DWORD* pcbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in LPCWSTR wzSrcPath, __in BOOL fSeek, __in DWORD cbStartPosition, @@ -851,7 +851,7 @@ extern "C" HRESULT DAPI FileReadPartial( ********************************************************************/ extern "C" HRESULT DAPI FileReadPartialEx( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) DWORD* pcbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in BOOL fSeek, __in DWORD cbStartPosition, @@ -990,7 +990,7 @@ extern "C" HRESULT DAPI FileWrite( __in_z LPCWSTR pwzFileName, __in DWORD dwFlagsAndAttributes, __in_bcount_opt(cbData) LPCBYTE pbData, - __in DWORD cbData, + __in SIZE_T cbData, __out_opt HANDLE* pHandle ) { @@ -1024,7 +1024,7 @@ LExit: extern "C" HRESULT DAPI FileWriteHandle( __in HANDLE hFile, __in_bcount_opt(cbData) LPCBYTE pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -1034,7 +1034,7 @@ extern "C" HRESULT DAPI FileWriteHandle( // Write out all of the data. do { - if (!::WriteFile(hFile, pbData + cbTotal, cbData - cbTotal, &cbDataWritten, NULL)) + if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)(cbData - cbTotal), &cbDataWritten, NULL)) { ExitOnLastError(hr, "Failed to write data to file handle."); } @@ -1700,7 +1700,7 @@ extern "C" HRESULT DAPI FileToString( { HRESULT hr = S_OK; BYTE *pbFullFileBuffer = NULL; - DWORD cbFullFileBuffer = 0; + SIZE_T cbFullFileBuffer = 0; BOOL fNullCharFound = FALSE; LPWSTR sczFileText = NULL; diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h index e61cdb58..f6d36e94 100644 --- a/src/dutil/inc/buffutil.h +++ b/src/dutil/inc/buffutil.h @@ -51,7 +51,7 @@ HRESULT BuffReadStream( HRESULT BuffWriteNumber( __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, - __in DWORD dw + __in DWORD_PTR dw ); HRESULT BuffWriteNumber64( __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h index ddae340f..d2b2f4fe 100644 --- a/src/dutil/inc/fileutil.h +++ b/src/dutil/inc/fileutil.h @@ -96,24 +96,24 @@ HRESULT DAPI FileRemoveFromPendingRename( ); HRESULT DAPI FileRead( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out DWORD* pcbDest, + __out SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath ); HRESULT DAPI FileReadEx( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out DWORD* pcbDest, + __out SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in DWORD dwShareMode ); HRESULT DAPI FileReadUntil( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) DWORD* pcbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in DWORD cbMaxRead ); HRESULT DAPI FileReadPartial( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) DWORD* pcbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in BOOL fSeek, __in DWORD cbStartPosition, @@ -122,7 +122,7 @@ HRESULT DAPI FileReadPartial( ); HRESULT DAPI FileReadPartialEx( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) DWORD* pcbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in BOOL fSeek, __in DWORD cbStartPosition, @@ -134,13 +134,13 @@ HRESULT DAPI FileWrite( __in_z LPCWSTR pwzFileName, __in DWORD dwFlagsAndAttributes, __in_bcount_opt(cbData) LPCBYTE pbData, - __in DWORD cbData, + __in SIZE_T cbData, __out_opt HANDLE* pHandle ); HRESULT DAPI FileWriteHandle( __in HANDLE hFile, __in_bcount_opt(cbData) LPCBYTE pbData, - __in DWORD cbData + __in SIZE_T cbData ); HRESULT DAPI FileCopyUsingHandles( __in HANDLE hSource, diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h index c73615aa..454506d2 100644 --- a/src/dutil/inc/strutil.h +++ b/src/dutil/inc/strutil.h @@ -46,29 +46,29 @@ HRESULT DAPI StrAnsiTrimWhitespace( HRESULT DAPI StrAllocString( __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrAllocStringSecure( __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrAnsiAllocString( __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in UINT uiCodepage ); HRESULT DAPI StrAllocStringAnsi( __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, __in_z LPCSTR szSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in UINT uiCodepage ); HRESULT DAPI StrAnsiAllocStringAnsi( __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, __in_z LPCSTR szSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrAllocPrefix( __deref_out_z LPWSTR* ppwz, @@ -78,17 +78,17 @@ HRESULT DAPI StrAllocPrefix( HRESULT DAPI StrAllocConcat( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrAllocConcatSecure( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrAnsiAllocConcat( __deref_out_z LPSTR* ppz, __in_z LPCSTR pzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ); HRESULT __cdecl StrAllocFormatted( __deref_out_z LPWSTR* ppwz, @@ -289,7 +289,7 @@ HRESULT DAPI StrArrayAllocString( __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, __inout LPUINT pcStrArray, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrArrayFree( diff --git a/src/dutil/inc/xmlutil.h b/src/dutil/inc/xmlutil.h index 3dc119bd..ba92ada9 100644 --- a/src/dutil/inc/xmlutil.h +++ b/src/dutil/inc/xmlutil.h @@ -53,7 +53,7 @@ HRESULT DAPI XmlLoadDocumentFromFile( ); HRESULT DAPI XmlLoadDocumentFromBuffer( __in_bcount(cbSource) const BYTE* pbSource, - __in DWORD cbSource, + __in SIZE_T cbSource, __out IXMLDOMDocument** ppixdDocument ); HRESULT DAPI XmlLoadDocumentFromFileEx( diff --git a/src/dutil/xmlutil.cpp b/src/dutil/xmlutil.cpp index e07c205d..f97ca962 100644 --- a/src/dutil/xmlutil.cpp +++ b/src/dutil/xmlutil.cpp @@ -416,7 +416,7 @@ LExit: *********************************************************************/ extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( __in_bcount(cbSource) const BYTE* pbSource, - __in DWORD cbSource, + __in SIZE_T cbSource, __out IXMLDOMDocument** ppixdDocument ) { @@ -447,7 +447,7 @@ extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE; sa.cbElements = 1; sa.pvData = (PVOID)pbSource; - sa.rgsabound[0].cElements = cbSource; + sa.rgsabound[0].cElements = (ULONG)cbSource; vtXmlSource.vt = VT_ARRAY | VT_UI1; vtXmlSource.parray = &sa; -- cgit v1.2.3-55-g6feb From 0ae3b1e7b5c0beff0fcfb82728c5bf9f25aee250 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sun, 29 Nov 2020 18:57:55 -0500 Subject: More size_t-ification. --- dutil.sln | 12 +--- src/dutil/buffutil.cpp | 59 ++++++++++++++++++- src/dutil/dirutil.cpp | 2 +- src/dutil/inc/buffutil.h | 11 ++++ src/dutil/inc/pathutil.h | 2 +- src/dutil/inc/strutil.h | 36 ++++++------ src/dutil/inc/verutil.h | 4 +- src/dutil/pathutil.cpp | 6 +- src/dutil/strutil.cpp | 144 +++++++++++++++++++++++------------------------ 9 files changed, 167 insertions(+), 109 deletions(-) diff --git a/dutil.sln b/dutil.sln index 3dafbc0c..433f42a5 100644 --- a/dutil.sln +++ b/dutil.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 MinimumVisualStudioVersion = 15.0.26124.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}" EndProject @@ -9,38 +9,30 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DUtilUnitTest", "src\test\D EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM.ActiveCfg = Debug|ARM - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM.Build.0 = Debug|ARM {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.Build.0 = Debug|ARM64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM.ActiveCfg = Release|ARM - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM.Build.0 = Release|ARM {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.ActiveCfg = Release|ARM64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.Build.0 = Release|ARM64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM.ActiveCfg = Debug|Win32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM64.ActiveCfg = Debug|Win32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x64.ActiveCfg = Debug|Win32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.ActiveCfg = Debug|Win32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.Build.0 = Debug|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM.ActiveCfg = Release|Win32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM64.ActiveCfg = Release|Win32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x64.ActiveCfg = Release|Win32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.ActiveCfg = Release|Win32 diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp index e78db24f..a70aaa81 100644 --- a/src/dutil/buffutil.cpp +++ b/src/dutil/buffutil.cpp @@ -51,11 +51,11 @@ LExit: } extern "C" HRESULT BuffReadNumber64( - __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in_bcount(cbBuffer) const BYTE * pbBuffer, __in SIZE_T cbBuffer, __inout SIZE_T* piBuffer, __out DWORD64* pdw64 - ) +) { Assert(pbBuffer); Assert(piBuffer); @@ -82,6 +82,38 @@ LExit: return hr; } +extern "C" HRESULT BuffReadPointer( + __in_bcount(cbBuffer) const BYTE * pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD_PTR* pdw64 +) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw64); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + ExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD_PTR) > cbAvailable) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw64 = *(const DWORD_PTR*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD_PTR); + +LExit: + return hr; +} + extern "C" HRESULT BuffReadString( __in_bcount(cbBuffer) const BYTE* pbBuffer, __in SIZE_T cbBuffer, @@ -300,6 +332,29 @@ LExit: return hr; } +extern "C" HRESULT BuffWritePointer( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD_PTR dw +) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD_PTR)); + ExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; + *piBuffer += sizeof(DWORD_PTR); + +LExit: + return hr; +} + extern "C" HRESULT BuffWriteString( __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp index 1f06f551..5e22ee65 100644 --- a/src/dutil/dirutil.cpp +++ b/src/dutil/dirutil.cpp @@ -346,7 +346,7 @@ extern "C" HRESULT DAPI DirGetCurrent( ) { HRESULT hr = S_OK; - DWORD_PTR cch = 0; + SIZE_T cch = 0; if (psczCurrentDirectory && *psczCurrentDirectory) { diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h index f6d36e94..a718e9c0 100644 --- a/src/dutil/inc/buffutil.h +++ b/src/dutil/inc/buffutil.h @@ -28,6 +28,12 @@ HRESULT BuffReadNumber64( __inout SIZE_T* piBuffer, __out DWORD64* pdw64 ); +HRESULT BuffReadPointer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD_PTR* pdw +); HRESULT BuffReadString( __in_bcount(cbBuffer) const BYTE* pbBuffer, __in SIZE_T cbBuffer, @@ -58,6 +64,11 @@ HRESULT BuffWriteNumber64( __inout SIZE_T* piBuffer, __in DWORD64 dw64 ); +HRESULT BuffWritePointer( + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD_PTR dw +); HRESULT BuffWriteString( __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h index 76798172..bee8ed1b 100644 --- a/src/dutil/inc/pathutil.h +++ b/src/dutil/inc/pathutil.h @@ -71,7 +71,7 @@ DAPI_(HRESULT) PathPrefix( ********************************************************************/ DAPI_(HRESULT) PathFixedBackslashTerminate( __inout_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD_PTR cchPath + __in SIZE_T cchPath ); /******************************************************************* diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h index 454506d2..187bfda8 100644 --- a/src/dutil/inc/strutil.h +++ b/src/dutil/inc/strutil.h @@ -19,11 +19,11 @@ extern "C" { HRESULT DAPI StrAlloc( __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch + __in SIZE_T cch ); HRESULT DAPI StrAllocSecure( __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch + __in SIZE_T cch ); HRESULT DAPI StrTrimCapacity( __deref_out_z LPWSTR* ppwz @@ -34,7 +34,7 @@ HRESULT DAPI StrTrimWhitespace( ); HRESULT DAPI StrAnsiAlloc( __deref_out_ecount_part(cch, 0) LPSTR* ppz, - __in DWORD_PTR cch + __in SIZE_T cch ); HRESULT DAPI StrAnsiTrimCapacity( __deref_out_z LPSTR* ppz @@ -73,7 +73,7 @@ HRESULT DAPI StrAnsiAllocStringAnsi( HRESULT DAPI StrAllocPrefix( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzPrefix, - __in DWORD_PTR cchPrefix + __in SIZE_T cchPrefix ); HRESULT DAPI StrAllocConcat( __deref_out_z LPWSTR* ppwz, @@ -139,11 +139,11 @@ HRESULT DAPI StrAllocFromError( HRESULT DAPI StrMaxLength( __in LPCVOID p, - __out DWORD_PTR* pcch + __out SIZE_T* pcbch ); HRESULT DAPI StrSize( __in LPCVOID p, - __out DWORD_PTR* pcb + __out SIZE_T* pcbb ); HRESULT DAPI StrFree( @@ -165,19 +165,19 @@ HRESULT DAPI StrReplaceString( HRESULT DAPI StrHexEncode( __in_ecount(cbSource) const BYTE* pbSource, - __in DWORD_PTR cbSource, + __in SIZE_T cbSource, __out_ecount(cchDest) LPWSTR wzDest, - __in DWORD_PTR cchDest + __in SIZE_T cchDest ); HRESULT DAPI StrAllocHexEncode( __in_ecount(cbSource) const BYTE* pbSource, - __in DWORD_PTR cbSource, + __in SIZE_T cbSource, __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest ); HRESULT DAPI StrHexDecode( __in_z LPCWSTR wzSource, __out_bcount(cbDest) BYTE* pbDest, - __in DWORD_PTR cbDest + __in SIZE_T cbDest ); HRESULT DAPI StrAllocHexDecode( __in_z LPCWSTR wzSource, @@ -187,29 +187,29 @@ HRESULT DAPI StrAllocHexDecode( HRESULT DAPI StrAllocBase85Encode( __in_bcount_opt(cbSource) const BYTE* pbSource, - __in DWORD_PTR cbSource, + __in SIZE_T cbSource, __deref_out_z LPWSTR* pwzDest ); HRESULT DAPI StrAllocBase85Decode( __in_z LPCWSTR wzSource, - __deref_out_bcount(*pcbDest) BYTE** hbDest, - __out DWORD_PTR* pcbDest - ); + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out SIZE_T* pcbDest +); HRESULT DAPI MultiSzLen( __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, - __out DWORD_PTR* pcch + __out SIZE_T* pcbch ); HRESULT DAPI MultiSzPrepend( __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt DWORD_PTR *pcchMultiSz, + __inout_opt SIZE_T* pcchMultiSz, __in __nullnullterminated LPCWSTR pwzInsert ); HRESULT DAPI MultiSzFindSubstring( __in __nullnullterminated LPCWSTR pwzMultiSz, __in __nullnullterminated LPCWSTR pwzSubstring, __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out_z LPCWSTR* ppwzFoundIn + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn ); HRESULT DAPI MultiSzFindString( __in __nullnullterminated LPCWSTR pwzMultiSz, @@ -223,7 +223,7 @@ HRESULT DAPI MultiSzRemoveString( ); HRESULT DAPI MultiSzInsertString( __deref_inout_z LPWSTR* ppwzMultiSz, - __inout_opt DWORD_PTR *pcchMultiSz, + __inout_opt SIZE_T* pcchMultiSz, __in DWORD_PTR dwIndex, __in_z LPCWSTR pwzInsert ); diff --git a/src/dutil/inc/verutil.h b/src/dutil/inc/verutil.h index d3715049..30869aef 100644 --- a/src/dutil/inc/verutil.h +++ b/src/dutil/inc/verutil.h @@ -12,7 +12,7 @@ typedef struct _VERUTIL_VERSION_RELEASE_LABEL { BOOL fNumeric; DWORD dwValue; - DWORD_PTR cchLabelOffset; + SIZE_T cchLabelOffset; int cchLabel; } VERUTIL_VERSION_RELEASE_LABEL; @@ -25,7 +25,7 @@ typedef struct _VERUTIL_VERSION DWORD dwRevision; DWORD cReleaseLabels; VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels; - DWORD_PTR cchMetadataOffset; + SIZE_T cchMetadataOffset; BOOL fInvalid; } VERUTIL_VERSION; diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp index c508dd32..d8894756 100644 --- a/src/dutil/pathutil.cpp +++ b/src/dutil/pathutil.cpp @@ -328,7 +328,7 @@ DAPI_(HRESULT) PathPrefix( HRESULT hr = S_OK; LPWSTR wzFullPath = *psczFullPath; - DWORD_PTR cbFullPath = 0; + SIZE_T cbFullPath = 0; if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && @@ -365,7 +365,7 @@ LExit: DAPI_(HRESULT) PathFixedBackslashTerminate( __inout_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD_PTR cchPath + __in SIZE_T cchPath ) { HRESULT hr = S_OK; @@ -396,7 +396,7 @@ DAPI_(HRESULT) PathBackslashTerminate( Assert(psczPath && *psczPath); HRESULT hr = S_OK; - DWORD_PTR cchPath = 0; + SIZE_T cchPath = 0; size_t cchLength = 0; hr = StrMaxLength(*psczPath, &cchPath); diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp index 4e184c34..c1d701d3 100644 --- a/src/dutil/strutil.cpp +++ b/src/dutil/strutil.cpp @@ -7,19 +7,19 @@ // Forward declarations. static HRESULT AllocHelper( __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch, + __in SIZE_T cch, __in BOOL fZeroOnRealloc ); static HRESULT AllocStringHelper( __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in BOOL fZeroOnRealloc ); static HRESULT AllocConcatHelper( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in BOOL fZeroOnRealloc ); static HRESULT AllocFormattedArgsHelper( @@ -42,7 +42,7 @@ NOTE: caller is responsible for freeing ppwz even if function fails ********************************************************************/ extern "C" HRESULT DAPI StrAlloc( __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch + __in SIZE_T cch ) { return AllocHelper(ppwz, cch, FALSE); @@ -57,7 +57,7 @@ NOTE: caller is responsible for freeing ppwz even if function fails ********************************************************************/ extern "C" HRESULT DAPI StrAllocSecure( __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch + __in SIZE_T cch ) { return AllocHelper(ppwz, cch, TRUE); @@ -72,7 +72,7 @@ NOTE: caller is responsible for freeing ppwz even if function fails ********************************************************************/ static HRESULT AllocHelper( __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch, + __in SIZE_T cch, __in BOOL fZeroOnRealloc ) { @@ -128,7 +128,7 @@ HRESULT DAPI StrTrimCapacity( Assert(ppwz); HRESULT hr = S_OK; - DWORD_PTR cchLen = 0; + SIZE_T cchLen = 0; hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); ExitOnFailure(hr, "Failed to calculate length of string"); @@ -201,7 +201,7 @@ NOTE: caller is responsible for freeing ppsz even if function fails ********************************************************************/ extern "C" HRESULT DAPI StrAnsiAlloc( __deref_out_ecount_part(cch, 0) LPSTR* ppsz, - __in DWORD_PTR cch + __in SIZE_T cch ) { Assert(ppsz && cch); @@ -246,7 +246,7 @@ HRESULT DAPI StrAnsiTrimCapacity( Assert(ppz); HRESULT hr = S_OK; - DWORD_PTR cchLen = 0; + SIZE_T cchLen = 0; #pragma prefast(push) #pragma prefast(disable:25068) @@ -324,7 +324,7 @@ NOTE: if cchSource == 0, length of wzSource is used instead extern "C" HRESULT DAPI StrAllocString( __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ) { return AllocStringHelper(ppwz, wzSource, cchSource, FALSE); @@ -342,7 +342,7 @@ NOTE: if cchSource == 0, length of wzSource is used instead extern "C" HRESULT DAPI StrAllocStringSecure( __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ) { return AllocStringHelper(ppwz, wzSource, cchSource, TRUE); @@ -360,14 +360,14 @@ NOTE: if cchSource == 0, length of wzSource is used instead static HRESULT AllocStringHelper( __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in BOOL fZeroOnRealloc ) { Assert(ppwz && wzSource); // && *wzSource); HRESULT hr = S_OK; - DWORD_PTR cch = 0; + SIZE_T cch = 0; if (*ppwz) { @@ -385,7 +385,7 @@ static HRESULT AllocStringHelper( cchSource = lstrlenW(wzSource); } - DWORD_PTR cchNeeded; + SIZE_T cchNeeded; hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator ExitOnFailure(hr, "source string is too long"); @@ -414,7 +414,7 @@ NOTE: if cchSource == 0, length of wzSource is used instead extern "C" HRESULT DAPI StrAnsiAllocString( __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in UINT uiCodepage ) { @@ -422,8 +422,8 @@ extern "C" HRESULT DAPI StrAnsiAllocString( HRESULT hr = S_OK; LPSTR psz = NULL; - DWORD_PTR cch = 0; - DWORD_PTR cchDest = cchSource; // at least enough + SIZE_T cch = 0; + SIZE_T cchDest = cchSource; // at least enough if (*ppsz) { @@ -494,7 +494,7 @@ NOTE: if cchSource == 0, length of wzSource is used instead extern "C" HRESULT DAPI StrAllocStringAnsi( __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, __in_z LPCSTR szSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in UINT uiCodepage ) { @@ -502,8 +502,8 @@ extern "C" HRESULT DAPI StrAllocStringAnsi( HRESULT hr = S_OK; LPWSTR pwz = NULL; - DWORD_PTR cch = 0; - DWORD_PTR cchDest = cchSource; // at least enough + SIZE_T cch = 0; + SIZE_T cchDest = cchSource; // at least enough if (*ppwz) { @@ -575,13 +575,13 @@ NOTE: if cchSource == 0, length of wzSource is used instead HRESULT DAPI StrAnsiAllocStringAnsi( __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, __in_z LPCSTR szSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ) { Assert(ppsz && szSource); // && *szSource); HRESULT hr = S_OK; - DWORD_PTR cch = 0; + SIZE_T cch = 0; if (*ppsz) { @@ -599,7 +599,7 @@ HRESULT DAPI StrAnsiAllocStringAnsi( cchSource = lstrlenA(szSource); } - DWORD_PTR cchNeeded; + SIZE_T cchNeeded; hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator ExitOnFailure(hr, "source string is too long"); @@ -632,14 +632,14 @@ NOTE: if cchPrefix == 0, length of wzPrefix is used instead extern "C" HRESULT DAPI StrAllocPrefix( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzPrefix, - __in DWORD_PTR cchPrefix + __in SIZE_T cchPrefix ) { Assert(ppwz && wzPrefix); HRESULT hr = S_OK; - DWORD_PTR cch = 0; - DWORD_PTR cchLen = 0; + SIZE_T cch = 0; + SIZE_T cchLen = 0; if (*ppwz) { @@ -672,8 +672,8 @@ extern "C" HRESULT DAPI StrAllocPrefix( if (*ppwz) { - DWORD_PTR cb = cch * sizeof(WCHAR); - DWORD_PTR cbPrefix = cchPrefix * sizeof(WCHAR); + SIZE_T cb = cch * sizeof(WCHAR); + SIZE_T cbPrefix = cchPrefix * sizeof(WCHAR); memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix); memcpy(*ppwz, wzPrefix, cbPrefix); @@ -699,7 +699,7 @@ NOTE: if cchSource == 0, length of wzSource is used instead extern "C" HRESULT DAPI StrAllocConcat( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ) { return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE); @@ -718,7 +718,7 @@ NOTE: if cchSource == 0, length of wzSource is used instead extern "C" HRESULT DAPI StrAllocConcatSecure( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ) { return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE); @@ -737,15 +737,15 @@ NOTE: if cchSource == 0, length of wzSource is used instead static HRESULT AllocConcatHelper( __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource, + __in SIZE_T cchSource, __in BOOL fZeroOnRealloc ) { Assert(ppwz && wzSource); // && *wzSource); HRESULT hr = S_OK; - DWORD_PTR cch = 0; - DWORD_PTR cchLen = 0; + SIZE_T cch = 0; + SIZE_T cchLen = 0; if (*ppwz) { @@ -801,14 +801,14 @@ NOTE: if cchSource == 0, length of pzSource is used instead extern "C" HRESULT DAPI StrAnsiAllocConcat( __deref_out_z LPSTR* ppz, __in_z LPCSTR pzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ) { Assert(ppz && pzSource); // && *pzSource); HRESULT hr = S_OK; - DWORD_PTR cch = 0; - DWORD_PTR cchLen = 0; + SIZE_T cch = 0; + SIZE_T cchLen = 0; if (*ppz) { @@ -842,7 +842,7 @@ extern "C" HRESULT DAPI StrAnsiAllocConcat( { cch = (cchSource + cchLen + 1) * 2; hr = StrAnsiAlloc(ppz, cch); - ExitOnFailure(hr, "failed to allocate string from string: %ls", pzSource); + ExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource); } if (*ppz) @@ -1138,7 +1138,7 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( Assert(ppsz && szFormat && *szFormat); HRESULT hr = S_OK; - DWORD_PTR cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; + SIZE_T cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; LPSTR pszOriginal = NULL; DWORD cchOriginal = 0; @@ -1183,7 +1183,7 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( } cch *= 2; hr = StrAnsiAlloc(ppsz, cch); - ExitOnFailure(hr, "failed to allocate string to format: %ls", szFormat); + ExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat); hr = S_FALSE; } } while (S_FALSE == hr); @@ -1247,7 +1247,7 @@ NOTE: assumes Unicode string ********************************************************************/ extern "C" HRESULT DAPI StrMaxLength( __in LPCVOID p, - __out DWORD_PTR* pcch + __out SIZE_T* pcch ) { Assert(pcch); @@ -1281,7 +1281,7 @@ StrSize - returns count of bytes in dynamic string p ********************************************************************/ extern "C" HRESULT DAPI StrSize( __in LPCVOID p, - __out DWORD_PTR* pcb + __out SIZE_T* pcb ) { Assert(p && pcb); @@ -1430,9 +1430,9 @@ NOTE: wzDest must have space for cbSource * 2 + 1 characters ****************************************************************************/ extern "C" HRESULT DAPI StrHexEncode( __in_ecount(cbSource) const BYTE* pbSource, - __in DWORD_PTR cbSource, + __in SIZE_T cbSource, __out_ecount(cchDest) LPWSTR wzDest, - __in DWORD_PTR cchDest + __in SIZE_T cchDest ) { Assert(pbSource && wzDest); @@ -1469,12 +1469,12 @@ StrAllocHexEncode - converts an array of bytes to an allocated text string ****************************************************************************/ HRESULT DAPI StrAllocHexEncode( __in_ecount(cbSource) const BYTE* pbSource, - __in DWORD_PTR cbSource, + __in SIZE_T cbSource, __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest ) { HRESULT hr = S_OK; - DWORD_PTR cchSource = sizeof(WCHAR) * (cbSource + 1); + SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1); hr = StrAlloc(ppwzDest, cchSource); ExitOnFailure(hr, "Failed to allocate hex string."); @@ -1495,7 +1495,7 @@ NOTE: wzSource must contain even number of characters extern "C" HRESULT DAPI StrHexDecode( __in_z LPCWSTR wzSource, __out_bcount(cbDest) BYTE* pbDest, - __in DWORD_PTR cbDest + __in SIZE_T cbDest ) { Assert(wzSource && pbDest); @@ -1612,12 +1612,12 @@ StrAllocBase85Encode - converts an array of bytes into an XML compatible string ****************************************************************************/ extern "C" HRESULT DAPI StrAllocBase85Encode( __in_bcount_opt(cbSource) const BYTE* pbSource, - __in DWORD_PTR cbSource, + __in SIZE_T cbSource, __deref_out_z LPWSTR* pwzDest ) { HRESULT hr = S_OK; - DWORD_PTR cchDest = 0; + SIZE_T cchDest = 0; LPWSTR wzDest; DWORD_PTR iSource = 0; DWORD_PTR iDest = 0; @@ -1709,15 +1709,15 @@ NOTE: Use MemFree() to release the allocated stream of bytes extern "C" HRESULT DAPI StrAllocBase85Decode( __in_z LPCWSTR wzSource, __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out DWORD_PTR* pcbDest + __out SIZE_T* pcbDest ) { HRESULT hr = S_OK; - DWORD_PTR cchSource = lstrlenW(wzSource); + SIZE_T cchSource = lstrlenW(wzSource); DWORD_PTR i, n, k; BYTE* pbDest; - DWORD_PTR cbDest; + SIZE_T cbDest; if (!wzSource || !ppbDest || !pcbDest) { @@ -1849,9 +1849,9 @@ including the double null terminator at the end of the MULTISZ. NOTE: returns 0 if the multisz in not properly terminated with two nulls ****************************************************************************/ extern "C" HRESULT DAPI MultiSzLen( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __out DWORD_PTR* pcch - ) + __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, + __out SIZE_T* pcch +) { Assert(pcch); @@ -1894,7 +1894,7 @@ MultiSzPrepend - prepends a string onto the front of a MUTLISZ ****************************************************************************/ extern "C" HRESULT DAPI MultiSzPrepend( __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt DWORD_PTR *pcchMultiSz, + __inout_opt SIZE_T* pcchMultiSz, __in __nullnullterminated LPCWSTR pwzInsert ) { @@ -1902,9 +1902,9 @@ extern "C" HRESULT DAPI MultiSzPrepend( HRESULT hr =S_OK; LPWSTR pwzResult = NULL; - DWORD_PTR cchResult = 0; - DWORD_PTR cchInsert = 0; - DWORD_PTR cchMultiSz = 0; + SIZE_T cchResult = 0; + SIZE_T cchInsert = 0; + SIZE_T cchMultiSz = 0; // Get the lengths of the MULTISZ (and prime it if it's not initialized) if (pcchMultiSz && 0 != *pcchMultiSz) @@ -1979,8 +1979,8 @@ extern "C" HRESULT DAPI MultiSzFindSubstring( HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty) LPCWSTR wz = pwzMultiSz; DWORD_PTR dwIndex = 0; - DWORD_PTR cchMultiSz = 0; - DWORD_PTR cchProgress = 0; + SIZE_T cchMultiSz = 0; + SIZE_T cchProgress = 0; hr = MultiSzLen(pwzMultiSz, &cchMultiSz); ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); @@ -2045,8 +2045,8 @@ extern "C" HRESULT DAPI MultiSzFindString( HRESULT hr = S_FALSE; // Assume we won't find it LPCWSTR wz = pwzMultiSz; DWORD_PTR dwIndex = 0; - DWORD_PTR cchMutliSz = 0; - DWORD_PTR cchProgress = 0; + SIZE_T cchMutliSz = 0; + SIZE_T cchProgress = 0; hr = MultiSzLen(pwzMultiSz, &cchMutliSz); ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); @@ -2112,8 +2112,8 @@ extern "C" HRESULT DAPI MultiSzRemoveString( LPCWSTR wz = *ppwzMultiSz; LPCWSTR wzNext = NULL; DWORD_PTR dwCurrentIndex = 0; - DWORD_PTR cchMultiSz = 0; - DWORD_PTR cchProgress = 0; + SIZE_T cchMultiSz = 0; + SIZE_T cchProgress = 0; hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); @@ -2179,7 +2179,7 @@ MultiSzInsertString - inserts new string at the specified index ****************************************************************************/ extern "C" HRESULT DAPI MultiSzInsertString( __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt DWORD_PTR *pcchMultiSz, + __inout_opt SIZE_T* pcchMultiSz, __in DWORD_PTR dwIndex, __in __nullnullterminated LPCWSTR pwzInsert ) @@ -2189,11 +2189,11 @@ extern "C" HRESULT DAPI MultiSzInsertString( HRESULT hr = S_OK; LPCWSTR wz = *ppwzMultiSz; DWORD_PTR dwCurrentIndex = 0; - DWORD_PTR cchProgress = 0; + SIZE_T cchProgress = 0; LPWSTR pwzResult = NULL; - DWORD_PTR cchResult = 0; - DWORD_PTR cchString = lstrlenW(pwzInsert); - DWORD_PTR cchMultiSz = 0; + SIZE_T cchResult = 0; + SIZE_T cchString = lstrlenW(pwzInsert); + SIZE_T cchMultiSz = 0; if (pcchMultiSz && 0 != *pcchMultiSz) { @@ -2302,7 +2302,7 @@ extern "C" LPCWSTR DAPI wcsistr( { LPCWSTR wzSource = wzString; LPCWSTR wzSearch = NULL; - DWORD_PTR cchSourceIndex = 0; + SIZE_T cchSourceIndex = 0; // Walk through wzString (the source string) one character at a time while (*wzSource) @@ -2600,7 +2600,7 @@ extern "C" HRESULT DAPI StrArrayAllocString( __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, __inout LPUINT pcStrArray, __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource + __in SIZE_T cchSource ) { HRESULT hr = S_OK; @@ -2726,7 +2726,7 @@ extern "C" DAPI_(HRESULT) StrSecureZeroString( ) { HRESULT hr = S_OK; - DWORD_PTR cch; + SIZE_T cch; if (pwz) { -- cgit v1.2.3-55-g6feb From c294fb860ed7710c80fc004af6c9ebb09779c70c Mon Sep 17 00:00:00 2001 From: Nir Bar Date: Mon, 11 Jan 2021 20:07:38 +0200 Subject: Add functions to start/end MSI transactions and check whether or not it is supported on the target machine (#22) * Add functions to start/end MSI transactions and check whether or not it is supported on the target machine * Add log mode parameter to MSI transaction functions. * No default log mode for WiuEndTransaction --- src/dutil/inc/wiutil.h | 24 ++++++++++++++++ src/dutil/wiutil.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/src/dutil/inc/wiutil.h b/src/dutil/inc/wiutil.h index 07f6b56c..10d003b0 100644 --- a/src/dutil/inc/wiutil.h +++ b/src/dutil/inc/wiutil.h @@ -226,6 +226,15 @@ typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)( __in LPCWSTR szSource, __in_opt DWORD dwIndex ); +typedef UINT(WINAPI* PFN_MSIBEGINTRANSACTIONW)( + __in LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE* phTransactionHandle, + __out HANDLE* phChangeOfOwnerEvent + ); +typedef UINT(WINAPI* PFN_MSIENDTRANSACTION)( + __in DWORD dwTransactionState + ); HRESULT DAPI WiuInitialize( @@ -372,6 +381,21 @@ HRESULT DAPI WiuSourceListAddSourceEx( __in_z LPCWSTR wzSource, __in_opt DWORD dwIndex ); +HRESULT DAPI WiuBeginTransaction( + __in_z LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE* phTransactionHandle, + __out HANDLE* phChangeOfOwnerEvent, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ); +HRESULT DAPI WiuEndTransaction( + __in DWORD dwTransactionState, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ); +BOOL DAPI WiuIsMsiTransactionSupported( + ); #ifdef __cplusplus } diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp index 1b3dd317..7336d685 100644 --- a/src/dutil/wiutil.cpp +++ b/src/dutil/wiutil.cpp @@ -42,6 +42,11 @@ static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL; static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL; + +// MSI Transactions v4.5+ +static PFN_MSIBEGINTRANSACTIONW vpfnMsiBeginTransaction = NULL; +static PFN_MSIENDTRANSACTION vpfnMsiEndTransaction = NULL; + static BOOL vfWiuInitialized = FALSE; // globals @@ -176,6 +181,17 @@ extern "C" HRESULT DAPI WiuInitialize( vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary; } + // MSI Transaction functions + if (NULL == vpfnMsiBeginTransaction) + { + vpfnMsiBeginTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiBeginTransactionW")); + } + + if (NULL == vpfnMsiEndTransaction) + { + vpfnMsiEndTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEndTransaction")); + } + vfWiuInitialized = TRUE; LExit: @@ -202,6 +218,8 @@ extern "C" void DAPI WiuUninitialize( vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; vpfnMsiSourceListAddSourceExWFromLibrary = NULL; + vpfnMsiBeginTransaction = NULL; + vpfnMsiEndTransaction = NULL; } vfWiuInitialized = FALSE; @@ -886,6 +904,63 @@ LExit: return hr; } +extern "C" BOOL DAPI WiuIsMsiTransactionSupported( + ) +{ + return vpfnMsiBeginTransaction && vpfnMsiEndTransaction; +} + +extern "C" HRESULT DAPI WiuBeginTransaction( + __in_z LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE * phTransactionHandle, + __out HANDLE * phChangeOfOwnerEvent, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!WiuIsMsiTransactionSupported()) + { + ExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + } + + hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); + ExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + + er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent); + ExitOnWin32Error(er, hr, "Failed to begin transaction."); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI WiuEndTransaction( + __in DWORD dwTransactionState, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!WiuIsMsiTransactionSupported()) + { + ExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + } + + hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); + ExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + + er = vpfnMsiEndTransaction(dwTransactionState); + ExitOnWin32Error(er, hr, "Failed to end transaction."); + +LExit: + return hr; +} + static DWORD CheckForRestartErrorCode( -- cgit v1.2.3-55-g6feb From 69567aaa95f41812a49afa7454b69a3d79c5010d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 7 Feb 2021 17:38:13 -0600 Subject: Add OsRtlGetVersion. #6318 --- src/dutil/inc/osutil.h | 3 +++ src/dutil/osutil.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/dutil/inc/osutil.h b/src/dutil/inc/osutil.h index 01f8d9cf..2cce6f63 100644 --- a/src/dutil/inc/osutil.h +++ b/src/dutil/inc/osutil.h @@ -33,6 +33,9 @@ HRESULT DAPI OsIsRunningPrivileged( HRESULT DAPI OsIsUacEnabled( __out BOOL* pfUacEnabled ); +HRESULT DAPI OsRtlGetVersion( + __inout RTL_OSVERSIONINFOEXW* pOvix + ); #ifdef __cplusplus } diff --git a/src/dutil/osutil.cpp b/src/dutil/osutil.cpp index d1a4dd9a..38b32eb3 100644 --- a/src/dutil/osutil.cpp +++ b/src/dutil/osutil.cpp @@ -2,8 +2,11 @@ #include "precomp.h" +typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); + OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; DWORD vdwOsServicePack = 0; +RTL_OSVERSIONINFOEXW vovix = { }; /******************************************************************** OsGetVersion @@ -186,3 +189,41 @@ LExit: return hr; } + +HRESULT DAPI OsRtlGetVersion( + __inout RTL_OSVERSIONINFOEXW* pOvix + ) +{ + HRESULT hr = S_OK; + HMODULE hNtdll = NULL; + PFN_RTL_GET_VERSION pfnRtlGetVersion = NULL; + + if (vovix.dwOSVersionInfoSize) + { + ExitFunction(); + } + + vovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + + hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll); + if (E_MODNOTFOUND == hr) + { + ExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); + } + ExitOnFailure(hr, "Failed to load ntdll.dll."); + + pfnRtlGetVersion = reinterpret_cast(::GetProcAddress(hNtdll, "RtlGetVersion")); + ExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); + + hr = static_cast(pfnRtlGetVersion(&vovix)); + +LExit: + memcpy(pOvix, &vovix, sizeof(RTL_OSVERSIONINFOEXW)); + + if (hNtdll) + { + ::FreeLibrary(hNtdll); + } + + return hr; +} -- cgit v1.2.3-55-g6feb From bee6a20d1c7d807b5023d932ae179db1bc9a0f80 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 7 Feb 2021 17:56:58 -0600 Subject: Ignore enum CA warnings and scope the osutil deprecation suppression. Also, fix project reference so the .vcx project system understands it. --- src/Cpp.Build.props | 4 ++++ src/CustomizedNativeRecommendedRules.ruleset | 8 ++++++++ src/dutil/dutil.vcxproj | 5 +---- src/dutil/osutil.cpp | 7 +++++++ src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 4 +++- 5 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 src/CustomizedNativeRecommendedRules.ruleset diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 44a042c7..fb805b42 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -12,6 +12,10 @@ $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset + + $(DisableSpecificCompilerWarnings) diff --git a/src/CustomizedNativeRecommendedRules.ruleset b/src/CustomizedNativeRecommendedRules.ruleset new file mode 100644 index 00000000..142b141c --- /dev/null +++ b/src/CustomizedNativeRecommendedRules.ruleset @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index 2cf99d13..b84fd80f 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -83,10 +83,7 @@ - - - 4996 - + diff --git a/src/dutil/osutil.cpp b/src/dutil/osutil.cpp index 38b32eb3..8834cd30 100644 --- a/src/dutil/osutil.cpp +++ b/src/dutil/osutil.cpp @@ -22,7 +22,14 @@ extern "C" void DAPI OsGetVersion( if (OS_VERSION_UNKNOWN == vOsVersion) { ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + +#pragma warning (push) +#pragma warning(suppress: 4996) // deprecated +#pragma warning (push) +#pragma warning(suppress: 28159)// deprecated, use other function instead ::GetVersionExW(reinterpret_cast(&ovi)); // only fails if version info size is set incorrectly. +#pragma warning (pop) +#pragma warning (pop) vdwOsServicePack = static_cast(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor; if (4 == ovi.dwMajorVersion) diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index e1164aca..942c39f0 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -70,7 +70,9 @@ - + + {1244E671-F108-4334-BA52-8A7517F26ECD} + -- cgit v1.2.3-55-g6feb From 931d4e4d641155ab0c90fe7717abb12db4736317 Mon Sep 17 00:00:00 2001 From: Andrij Abyzov Date: Mon, 8 Feb 2021 20:00:00 +0100 Subject: WIXBUG4931 Fix drawing of image static controls --- src/dutil/thmutil.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 6c9c5cd6..9b9bf15e 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -40,6 +40,7 @@ const DWORD GROW_FONT_INSTANCES = 3; const DWORD GROW_WINDOW_TEXT = 250; const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink"; const LPCWSTR THEME_WC_PANEL = L"ThemePanel"; +const LPCWSTR THEME_WC_STATICOWNERDRAW = L"ThemeStaticOwnerDraw"; static Gdiplus::GdiplusStartupInput vgsi; static Gdiplus::GdiplusStartupOutput vgso = { }; @@ -47,6 +48,8 @@ static ULONG_PTR vgdiToken = 0; static ULONG_PTR vgdiHookToken = 0; static HMODULE vhHyperlinkRegisteredModule = NULL; static HMODULE vhPanelRegisteredModule = NULL; +static HMODULE vhStaticOwnerDrawRegisteredModule = NULL; +static WNDPROC vpfnStaticOwnerDrawBaseWndProc = NULL; static HMODULE vhModuleMsftEdit = NULL; static HMODULE vhModuleRichEd = NULL; static HCURSOR vhCursorHand = NULL; @@ -348,6 +351,12 @@ static LRESULT CALLBACK PanelWndProc( __in WPARAM wParam, __in LPARAM lParam ); +static LRESULT CALLBACK StaticOwnerDrawWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); static HRESULT LocalizeControls( __in DWORD cControls, __in THEME_CONTROL* rgControls, @@ -479,6 +488,13 @@ DAPI_(void) ThemeUninitialize() vhPanelRegisteredModule = NULL; } + if (vhStaticOwnerDrawRegisteredModule) + { + ::UnregisterClassW(THEME_WC_STATICOWNERDRAW, vhStaticOwnerDrawRegisteredModule); + vhStaticOwnerDrawRegisteredModule = NULL; + vpfnStaticOwnerDrawBaseWndProc = NULL; + } + if (vgdiToken) { GdipUninitialize(vgdiToken); @@ -1598,6 +1614,8 @@ static HRESULT RegisterWindowClasses( HRESULT hr = S_OK; WNDCLASSW wcHyperlink = { }; WNDCLASSW wcPanel = { }; + WNDCLASSW wcStaticOwnerDraw = { }; + WNDPROC pfnStaticOwnerDrawBaseWndProc = NULL; vhCursorHand = ::LoadCursorA(NULL, IDC_HAND); @@ -1630,6 +1648,22 @@ static HRESULT RegisterWindowClasses( } vhPanelRegisteredModule = hModule; + if (!::GetClassInfoW(NULL, WC_STATICW, &wcStaticOwnerDraw)) + { + ThmExitWithLastError(hr, "Failed to get static window class."); + } + + pfnStaticOwnerDrawBaseWndProc = wcStaticOwnerDraw.lpfnWndProc; + wcStaticOwnerDraw.lpfnWndProc = StaticOwnerDrawWndProc; + wcStaticOwnerDraw.hInstance = hModule; + wcStaticOwnerDraw.lpszClassName = THEME_WC_STATICOWNERDRAW; + if (!::RegisterClassW(&wcStaticOwnerDraw)) + { + ThmExitWithLastError(hr, "Failed to register OwnerDraw window class."); + } + vhStaticOwnerDrawRegisteredModule = hModule; + vpfnStaticOwnerDrawBaseWndProc = pfnStaticOwnerDrawBaseWndProc; + LExit: return hr; @@ -4919,6 +4953,21 @@ static LRESULT CALLBACK PanelWndProc( return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); } +static LRESULT CALLBACK StaticOwnerDrawWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_UPDATEUISTATE: + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + default: + return (*vpfnStaticOwnerDrawBaseWndProc)(hWnd, uMsg, wParam, lParam); + } +} static HRESULT LoadControls( __in THEME* pTheme, @@ -5011,7 +5060,7 @@ static HRESULT LoadControls( case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps). if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) { - wzWindowClass = WC_STATICW; + wzWindowClass = THEME_WC_STATICOWNERDRAW; dwWindowBits |= SS_OWNERDRAW; pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; } @@ -5038,7 +5087,7 @@ static HRESULT LoadControls( case THEME_CONTROL_TYPE_PROGRESSBAR: if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) { - wzWindowClass = WC_STATICW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control. + wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control. dwWindowBits |= SS_OWNERDRAW; pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; } -- cgit v1.2.3-55-g6feb From 3bbf1347b900ec115a12faf8f46965c9b7649696 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 2 Mar 2021 14:18:38 -0600 Subject: Add SHA512 support to apuputil. #3992 --- src/dutil/apuputil.cpp | 35 +++++++++++++++++++++++++---------- src/dutil/inc/apuputil.h | 1 + src/dutil/inc/cryputil.h | 3 +++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp index 5bbdb8dd..bf655ecc 100644 --- a/src/dutil/apuputil.cpp +++ b/src/dutil/apuputil.cpp @@ -2,13 +2,11 @@ #include "precomp.h" -#define SHA256_DIGEST_LEN 32 - // prototypes static HRESULT ProcessEntry( __in ATOM_ENTRY* pAtomEntry, __in LPCWSTR wzDefaultAppId, - __out APPLICATION_UPDATE_ENTRY* pApupEntry + __inout APPLICATION_UPDATE_ENTRY* pApupEntry ); static HRESULT ParseEnclosure( __in ATOM_LINK* pLink, @@ -192,7 +190,7 @@ extern "C" void DAPI ApupFreeChain( static HRESULT ProcessEntry( __in ATOM_ENTRY* pAtomEntry, __in LPCWSTR wzDefaultAppId, - __out APPLICATION_UPDATE_ENTRY* pApupEntry + __inout APPLICATION_UPDATE_ENTRY* pApupEntry ) { HRESULT hr = S_OK; @@ -325,6 +323,9 @@ static HRESULT ParseEnclosure( ) { HRESULT hr = S_OK; + DWORD dwDigestLength = 0; + DWORD dwDigestStringLength = 0; + size_t cchDigestString = 0; // First search the ATOM link's custom elements to try and find the application update enclosure information. for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext) @@ -333,36 +334,50 @@ static HRESULT ParseEnclosure( { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1)) { - // Find the digest[@algorithm='sha256'] which is required. Everything else is ignored. + // Find the digest[@algorithm] which is required. Everything else is ignored. for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) { + dwDigestLength = 0; if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1)) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1)) { pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5; + dwDigestLength = MD5_HASH_LEN; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1)) { pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1; + dwDigestLength = SHA1_HASH_LEN; } - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1)) + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1)) { pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256; + dwDigestLength = SHA256_HASH_LEN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha512", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA512; + dwDigestLength = SHA512_HASH_LEN; } break; } } - if (APUP_HASH_ALGORITHM_SHA256 == pEnclosure->digestAlgorithm) + if (dwDigestLength) { - if (64 != lstrlenW(pElement->wzValue)) + dwDigestStringLength = 2 * dwDigestLength; + + hr = ::StringCchLengthW(pElement->wzValue, STRSAFE_MAX_CCH, &cchDigestString); + ExitOnFailure(hr, "Failed to get string length of digest value."); + + if (dwDigestStringLength != cchDigestString) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Invalid digest length for SHA256 algorithm."); + ExitOnRootFailure(hr, "Invalid digest length (%zu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); } - pEnclosure->cbDigest = sizeof(BYTE) * SHA256_DIGEST_LEN; + pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength; pEnclosure->rgbDigest = static_cast(MemAlloc(pEnclosure->cbDigest, TRUE)); ExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); diff --git a/src/dutil/inc/apuputil.h b/src/dutil/inc/apuputil.h index 15d42f5d..f26a12b7 100644 --- a/src/dutil/inc/apuputil.h +++ b/src/dutil/inc/apuputil.h @@ -18,6 +18,7 @@ typedef enum APUP_HASH_ALGORITHM APUP_HASH_ALGORITHM_MD5, APUP_HASH_ALGORITHM_SHA1, APUP_HASH_ALGORITHM_SHA256, + APUP_HASH_ALGORITHM_SHA512, } APUP_HASH_ALGORITHM; diff --git a/src/dutil/inc/cryputil.h b/src/dutil/inc/cryputil.h index 88aa784d..02492d8a 100644 --- a/src/dutil/inc/cryputil.h +++ b/src/dutil/inc/cryputil.h @@ -11,7 +11,10 @@ extern "C" { // Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE. #define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE +#define MD5_HASH_LEN 16 #define SHA1_HASH_LEN 20 +#define SHA256_HASH_LEN 32 +#define SHA512_HASH_LEN 64 typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)( __inout PVOID Memory, -- cgit v1.2.3-55-g6feb From 10ebf674da5df9224e4eddd3545518434c5b455b Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 2 Mar 2021 14:19:14 -0600 Subject: Update rest of dutil to use their own source with the Exit* macros. Fix some CA warnings. --- src/dutil/acl2util.cpp | 30 ++++-- src/dutil/aclutil.cpp | 126 ++++++++++++---------- src/dutil/apputil.cpp | 21 +++- src/dutil/apuputil.cpp | 116 ++++++++++++--------- src/dutil/atomutil.cpp | 233 +++++++++++++++++++++-------------------- src/dutil/buffutil.cpp | 99 ++++++++++-------- src/dutil/cabcutil.cpp | 218 ++++++++++++++++++++------------------ src/dutil/cabutil.cpp | 96 ++++++++++------- src/dutil/certutil.cpp | 59 +++++++---- src/dutil/conutil.cpp | 77 ++++++++------ src/dutil/cryputil.cpp | 63 ++++++----- src/dutil/deputil.cpp | 145 ++++++++++++++------------ src/dutil/dictutil.cpp | 111 +++++++++++--------- src/dutil/dirutil.cpp | 61 +++++++---- src/dutil/dlutil.cpp | 89 +++++++++------- src/dutil/dutil.cpp | 29 ++++-- src/dutil/eseutil.cpp | 101 ++++++++++-------- src/dutil/fileutil.cpp | 265 +++++++++++++++++++++++++---------------------- src/dutil/gdiputil.cpp | 35 +++++-- src/dutil/guidutil.cpp | 23 +++- src/dutil/iis7util.cpp | 57 ++++++---- src/dutil/inc/atomutil.h | 2 +- src/dutil/inc/buffutil.h | 14 +-- src/dutil/inc/conutil.h | 4 +- src/dutil/inc/deputil.h | 2 +- src/dutil/inc/dutil.h | 2 +- src/dutil/inc/eseutil.h | 2 +- src/dutil/inc/fileutil.h | 2 +- src/dutil/inc/inetutil.h | 2 +- src/dutil/inc/iniutil.h | 2 +- src/dutil/inc/memutil.h | 6 +- src/dutil/inc/pathutil.h | 6 +- src/dutil/inc/regutil.h | 8 +- src/dutil/inc/shelutil.h | 6 +- src/dutil/inc/strutil.h | 4 +- src/dutil/inc/thmutil.h | 2 +- src/dutil/inc/uriutil.h | 2 +- src/dutil/inc/wiutil.h | 2 +- src/dutil/inetutil.cpp | 37 +++++-- src/dutil/iniutil.cpp | 107 +++++++++++-------- src/dutil/jsonutil.cpp | 79 ++++++++------ src/dutil/locutil.cpp | 125 ++++++++++++---------- src/dutil/logutil.cpp | 67 +++++++----- src/dutil/memutil.cpp | 51 +++++---- src/dutil/metautil.cpp | 37 +++++-- src/dutil/monutil.cpp | 205 +++++++++++++++++++----------------- src/dutil/osutil.cpp | 29 ++++-- src/dutil/path2utl.cpp | 21 +++- src/dutil/pathutil.cpp | 175 +++++++++++++++++-------------- src/dutil/perfutil.cpp | 15 +++ src/dutil/polcutil.cpp | 27 +++-- src/dutil/proc2utl.cpp | 21 +++- src/dutil/proc3utl.cpp | 27 +++-- src/dutil/procutil.cpp | 57 ++++++---- src/dutil/regutil.cpp | 137 +++++++++++++----------- src/dutil/resrutil.cpp | 45 +++++--- src/dutil/reswutil.cpp | 65 +++++++----- src/dutil/rexutil.cpp | 59 +++++++---- src/dutil/rmutil.cpp | 63 ++++++----- src/dutil/rssutil.cpp | 125 ++++++++++++---------- src/dutil/shelutil.cpp | 69 +++++++----- src/dutil/sqlutil.cpp | 119 +++++++++++---------- src/dutil/srputil.cpp | 43 +++++--- src/dutil/strutil.cpp | 215 ++++++++++++++++++++------------------ src/dutil/svcutil.cpp | 21 +++- src/dutil/thmutil.cpp | 8 +- src/dutil/timeutil.cpp | 37 +++++-- src/dutil/uncutil.cpp | 25 ++++- src/dutil/uriutil.cpp | 93 ++++++++++------- src/dutil/userutil.cpp | 61 +++++++---- src/dutil/wiutil.cpp | 121 ++++++++++++---------- src/dutil/wuautil.cpp | 31 ++++-- src/dutil/xmlutil.cpp | 193 ++++++++++++++++++---------------- 73 files changed, 2779 insertions(+), 1953 deletions(-) diff --git a/src/dutil/acl2util.cpp b/src/dutil/acl2util.cpp index 2261abe3..5aba60f0 100644 --- a/src/dutil/acl2util.cpp +++ b/src/dutil/acl2util.cpp @@ -2,6 +2,20 @@ #include "precomp.h" +// Exit macros +#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) +#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) + /******************************************************************** AclCalculateServiceSidString - gets the SID string for the given service name @@ -26,17 +40,17 @@ extern "C" HRESULT DAPI AclCalculateServiceSidString( if (0 == cchServiceName) { hr = ::StringCchLengthW(wzServiceName, INT_MAX, reinterpret_cast(&cchServiceName)); - ExitOnFailure(hr, "Failed to get the length of the service name."); + AclExitOnFailure(hr, "Failed to get the length of the service name."); } hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName); - ExitOnFailure(hr, "Failed to upper case the service name."); + AclExitOnFailure(hr, "Failed to upper case the service name."); pbHash = reinterpret_cast(MemAlloc(cbHash, TRUE)); - ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); + AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * 2, PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); - ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); + AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u", MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])), @@ -80,7 +94,7 @@ extern "C" HRESULT DAPI AclGetAccountSidStringEx( if (!::ConvertSidToStringSidW(psid, &pwz)) { - ExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount); + AclExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount); } hr = StrAllocString(psczSid, pwz, 0); @@ -90,20 +104,20 @@ extern "C" HRESULT DAPI AclGetAccountSidStringEx( if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) { HRESULT hrLength = ::StringCchLengthW(wzAccount, INT_MAX, reinterpret_cast(&cchAccount)); - ExitOnFailure(hrLength, "Failed to get the length of the account name."); + AclExitOnFailure(hrLength, "Failed to get the length of the account name."); if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11)) { // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it. LPCWSTR wzServiceName = &wzAccount[11]; hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid); - ExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName); + AclExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName); *psczSid = sczSid; sczSid = NULL; } } - ExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount); + AclExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount); } LExit: diff --git a/src/dutil/aclutil.cpp b/src/dutil/aclutil.cpp index fc01ecc8..c9733033 100644 --- a/src/dutil/aclutil.cpp +++ b/src/dutil/aclutil.cpp @@ -2,6 +2,20 @@ #include "precomp.h" +// Exit macros +#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) +#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) + /******************************************************************** AclCheckAccess - determines if token has appropriate privileges @@ -18,25 +32,25 @@ extern "C" HRESULT DAPI AclCheckAccess( PSID psid = NULL; BOOL fIsMember = FALSE; - ExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check"); + AclExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check"); Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask); if (paa->pwzAccountName) { hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid); - ExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName); + AclExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName); } else { 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)) { - ExitWithLastError(hr, "failed to initialize SID"); + AclExitWithLastError(hr, "failed to initialize SID"); } } if (!::CheckTokenMembership(hToken, psid, &fIsMember)) { - ExitWithLastError(hr, "failed to check membership"); + AclExitWithLastError(hr, "failed to check membership"); } fIsMember ? hr = S_OK : hr = S_FALSE; @@ -123,7 +137,7 @@ extern "C" HRESULT DAPI AclGetWellKnownSid( // allocate memory for the SID and get it // psid = static_cast(MemAlloc(cbSid, TRUE)); - ExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID"); + AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID"); #if(_WIN32_WINNT < 0x0501) switch (wkst) @@ -160,19 +174,19 @@ extern "C" HRESULT DAPI AclGetWellKnownSid( break; default: hr = E_INVALIDARG; - ExitOnFailure(hr, "unknown well known SID: %d", wkst); + AclExitOnFailure(hr, "unknown well known SID: %d", wkst); } if (!fSuccess) - ExitOnLastError(hr, "failed to allocate well known SID: %d", wkst); + AclExitOnLastError(hr, "failed to allocate well known SID: %d", wkst); if (!::CopySid(cbSid, psid, psidTemp)) - ExitOnLastError(hr, "failed to create well known SID: %d", wkst); + AclExitOnLastError(hr, "failed to create well known SID: %d", wkst); #else Assert(NULL == psidTemp); if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid)) { - ExitWithLastError(hr, "failed to create well known SID: %d", wkst); + AclExitWithLastError(hr, "failed to create well known SID: %d", wkst); } #endif @@ -216,9 +230,9 @@ extern "C" HRESULT DAPI AclGetAccountSid( // allocate memory for the SID and domain name // psid = static_cast(MemAlloc(cbSid, TRUE)); - ExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID"); + AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID"); hr = StrAlloc(&pwzDomainName, cbDomainName); - ExitOnFailure(hr, "failed to allocate string for domain name"); + AclExitOnFailure(hr, "failed to allocate string for domain name"); // // try to lookup the account now @@ -232,24 +246,24 @@ extern "C" HRESULT DAPI AclGetAccountSid( if (SECURITY_MAX_SID_SIZE < cbSid) { PSID psidNew = static_cast(MemReAlloc(psid, cbSid, TRUE)); - ExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount); + AclExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount); psid = psidNew; } if (255 < cbDomainName) { hr = StrAlloc(&pwzDomainName, cbDomainName); - ExitOnFailure(hr, "failed to allocate string for domain name"); + AclExitOnFailure(hr, "failed to allocate string for domain name"); } if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) { - ExitWithLastError(hr, "failed to lookup account: %ls", wzAccount); + AclExitWithLastError(hr, "failed to lookup account: %ls", wzAccount); } } else { - ExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount); + AclExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount); } } @@ -284,12 +298,12 @@ extern "C" HRESULT DAPI AclGetAccountSidString( *ppwzSid = NULL; hr = AclGetAccountSid(wzSystem, wzAccount, &psid); - ExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount); + AclExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount); Assert(::IsValidSid(psid)); if (!::ConvertSidToStringSidW(psid, &pwz)) { - ExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount); + AclExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount); } hr = StrAllocString(ppwzSid, pwz, 0); @@ -347,14 +361,14 @@ extern "C" HRESULT DAPI AclCreateDacl( } pAcl = static_cast(MemAlloc(cbAcl, TRUE)); - ExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); + AclExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); #pragma prefast(push) #pragma prefast(disable:25029) if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION)) #pragma prefast(pop) { - ExitWithLastError(hr, "failed to initialize ACL"); + AclExitWithLastError(hr, "failed to initialize ACL"); } // add in the ACEs (denied first) @@ -365,7 +379,7 @@ extern "C" HRESULT DAPI AclCreateDacl( if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid)) #pragma prefast(pop) { - ExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i); + AclExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i); } } for (i = 0; i < cAllow; ++i) @@ -375,7 +389,7 @@ extern "C" HRESULT DAPI AclCreateDacl( if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid)) #pragma prefast(pop) { - ExitWithLastError(hr, "failed to add access allowed ACE #$d to ACL", i); + AclExitWithLastError(hr, "failed to add access allowed ACE #%d to ACL", i); } } @@ -422,7 +436,7 @@ extern "C" HRESULT DAPI AclAddToDacl( // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay) if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation)) { - ExitWithLastError(hr, "failed to get information about original ACL"); + AclExitWithLastError(hr, "failed to get information about original ACL"); } if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow @@ -430,29 +444,29 @@ extern "C" HRESULT DAPI AclAddToDacl( (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE)) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny)); + AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny)); } paaNewDeny = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE)); - ExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs"); + AclExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs"); if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow (asi.AceCount + cAllow) < cAllow || // check for overflow (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE)) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow)); + AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow)); } paaNewAllow = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE)); - ExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs"); + AclExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs"); // fill in the new structures with old data then new data (denied first) for (i = 0; i < asi.AceCount; ++i) { if (!::GetAce(pAcl, i, reinterpret_cast(&pada))) { - ExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); } if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType) @@ -474,7 +488,7 @@ extern "C" HRESULT DAPI AclAddToDacl( { if (!::GetAce(pAcl, i, reinterpret_cast(&paaa))) { - ExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); } if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType) @@ -493,7 +507,7 @@ extern "C" HRESULT DAPI AclAddToDacl( // create the dacl with the new hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew); - ExitOnFailure(hr, "failed to create new ACL from existing ACL"); + AclExitOnFailure(hr, "failed to create new ACL from existing ACL"); AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL"); Assert(S_OK == hr); @@ -551,9 +565,9 @@ extern "C" HRESULT DAPI AclCreateDaclOld( // create the SIDs and calculate the space for the ACL // pdwAccessMask = static_cast(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE)); - ExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask"); + AclExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask"); ppsid = static_cast(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE)); - ExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid"); + AclExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid"); cbAcl = sizeof (ACL); // start with the size of the header for (i = 0; i < cAclAccesses; ++i) @@ -561,7 +575,7 @@ extern "C" HRESULT DAPI AclCreateDaclOld( if (paa[i].pwzAccountName) { hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i); - ExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName); + AclExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName); } else { @@ -572,7 +586,7 @@ extern "C" HRESULT DAPI AclCreateDaclOld( paa[i].nSubAuthority[6], paa[i].nSubAuthority[7], (void**)(ppsid + i)))) { - ExitWithLastError(hr, "failed to initialize SIDs #%u", i); + AclExitWithLastError(hr, "failed to initialize SIDs #%u", i); } } @@ -594,14 +608,14 @@ extern "C" HRESULT DAPI AclCreateDaclOld( // allocate the ACL and set the appropriate ACEs // *ppACL = static_cast(MemAlloc(cbAcl, FALSE)); - ExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL"); + AclExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL"); #pragma prefast(push) #pragma prefast(disable:25029) if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION)) #pragma prefast(pop) { - ExitWithLastError(hr, "failed to initialize ACLs"); + AclExitWithLastError(hr, "failed to initialize ACLs"); } // add an access-allowed ACE for each of the SIDs @@ -614,7 +628,7 @@ extern "C" HRESULT DAPI AclCreateDaclOld( if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) #pragma prefast(pop) { - ExitWithLastError(hr, "failed to add access denied for ACE"); + AclExitWithLastError(hr, "failed to add access denied for ACE"); } } else @@ -624,7 +638,7 @@ extern "C" HRESULT DAPI AclCreateDaclOld( if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) #pragma prefast(pop) { - ExitWithLastError(hr, "failed to add access allowed for ACE"); + AclExitWithLastError(hr, "failed to add access allowed for ACE"); } } } @@ -669,8 +683,8 @@ extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( SECURITY_DESCRIPTOR sd; DWORD cbSD; - ExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided"); - ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided"); + AclExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided"); + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided"); *ppsd = NULL; @@ -687,7 +701,7 @@ extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE))) #pragma prefast(pop) { - ExitWithLastError(hr, "failed to initialize security descriptor"); + AclExitWithLastError(hr, "failed to initialize security descriptor"); } // @@ -695,7 +709,7 @@ extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( // cbSD = ::GetSecurityDescriptorLength(&sd); *ppsd = static_cast(MemAlloc(cbSD, FALSE)); - ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD); Assert(::IsValidSecurityDescriptor(*ppsd)); @@ -734,7 +748,7 @@ extern "C" HRESULT DAPI AclCreateSecurityDescriptor( // create the DACL // hr = AclCreateDaclOld(paa, cAclAccesses, &pACL); - ExitOnFailure(hr, "failed to create DACL for security descriptor"); + AclExitOnFailure(hr, "failed to create DACL for security descriptor"); // // create self-relative security descriptor @@ -770,15 +784,15 @@ extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString( va_start(args, wzSddlFormat); hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args); va_end(args); - ExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat); + AclExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat); if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD)) { - ExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl); + AclExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl); } *ppsd = static_cast(MemAlloc(cbSD, FALSE)); - ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor"); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor"); memcpy(*ppsd, psd, cbSD); Assert(::IsValidSecurityDescriptor(*ppsd)); @@ -815,7 +829,7 @@ extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor( HRESULT hr = S_OK; DWORD cbSD; - ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided"); + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided"); *ppsd = NULL; // @@ -823,7 +837,7 @@ extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor( // cbSD = ::GetSecurityDescriptorLength(psd); *ppsd = static_cast(MemAlloc(cbSD, 0)); - ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); memcpy(*ppsd, psd, cbSD); Assert(::IsValidSecurityDescriptor(*ppsd)); @@ -856,18 +870,18 @@ extern "C" HRESULT DAPI AclGetSecurityDescriptor( PSECURITY_DESCRIPTOR psd = NULL; DWORD cbSD; - ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided"); + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided"); *ppsd = NULL; // get the security descriptor for the object er = ::GetNamedSecurityInfoW(const_cast(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd); - ExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject); + AclExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject); Assert(::IsValidSecurityDescriptor(psd)); // copy the self-relative security descriptor cbSD = ::GetSecurityDescriptorLength(psd); *ppsd = static_cast(MemAlloc(cbSD, 0)); - ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); memcpy(*ppsd, psd, cbSD); Assert(::IsValidSecurityDescriptor(*ppsd)); @@ -905,7 +919,7 @@ extern "C" HRESULT DAPI AclSetSecurityWithRetry( DWORD i = 0; hr = StrAllocString(&sczObject, wzObject, 0); - ExitOnFailure(hr, "Failed to copy object to secure."); + AclExitOnFailure(hr, "Failed to copy object to secure."); hr = E_FAIL; for (i = 0; FAILED(hr) && i <= cRetry; ++i) @@ -918,7 +932,7 @@ extern "C" HRESULT DAPI AclSetSecurityWithRetry( DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl); hr = HRESULT_FROM_WIN32(er); } - ExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i); + AclExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i); LExit: ReleaseStr(sczObject); @@ -996,20 +1010,20 @@ extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor( if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid) { - ExitOnLastError(hr, "Failed to get acl from security descriptor"); + AclExitOnLastError(hr, "Failed to get acl from security descriptor"); } hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid); - ExitOnFailure(hr, "failed to get sid for Administrators group"); + AclExitOnFailure(hr, "failed to get sid for Administrators group"); ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE; ace[0].dwMask = GENERIC_ALL; hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew); - ExitOnFailure(hr, "failed to add Administrators ACE to ACL"); + AclExitOnFailure(hr, "failed to add Administrators ACE to ACL"); hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew); - ExitOnLastError(hr, "Failed to create new security descriptor"); + AclExitOnLastError(hr, "Failed to create new security descriptor"); // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it. pAclNew = NULL; diff --git a/src/dutil/apputil.cpp b/src/dutil/apputil.cpp index 8562a47a..589a09dd 100644 --- a/src/dutil/apputil.cpp +++ b/src/dutil/apputil.cpp @@ -2,6 +2,20 @@ #include "precomp.h" +// Exit macros +#define AppExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) +#define AppExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) +#define AppExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) +#define AppExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) +#define AppExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APPUTIL, e, x, s, __VA_ARGS__) +#define AppExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APPUTIL, g, x, s, __VA_ARGS__) + const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); @@ -33,6 +47,7 @@ extern "C" void DAPI AppInitialize( // Best effort call to initialize default DLL directories to system only. HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32"); + Assert(hKernel32); LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories"); if (pfnSetDefaultDllDirectories) { @@ -90,13 +105,13 @@ extern "C" DAPI_(HRESULT) AppParseCommandLine( // which fails pretty miserably if your first argument is something like // FOO="C:\Program Files\My Company". So give it something harmless to play with. hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0); - ExitOnFailure(hr, "Failed to initialize command line."); + AppExitOnFailure(hr, "Failed to initialize command line."); hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0); - ExitOnFailure(hr, "Failed to copy command line."); + AppExitOnFailure(hr, "Failed to copy command line."); argv = ::CommandLineToArgvW(sczCommandLine, &argc); - ExitOnNullWithLastError(argv, hr, "Failed to parse command line."); + AppExitOnNullWithLastError(argv, hr, "Failed to parse command line."); // Skip "ignored" argument/hack. *pArgv = argv + 1; diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp index bf655ecc..07d591a7 100644 --- a/src/dutil/apuputil.cpp +++ b/src/dutil/apuputil.cpp @@ -2,6 +2,20 @@ #include "precomp.h" +// Exit macros +#define ApupExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) +#define ApupExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) +#define ApupExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) +#define ApupExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) +#define ApupExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APUPUTIL, e, x, s, __VA_ARGS__) +#define ApupExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APUPUTIL, g, x, s, __VA_ARGS__) + // prototypes static HRESULT ProcessEntry( __in ATOM_ENTRY* pAtomEntry, @@ -61,14 +75,14 @@ extern "C" HRESULT DAPI ApupAllocChainFromAtom( if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) { hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0); - ExitOnFailure(hr, "Failed to allocate default application id."); + ApupExitOnFailure(hr, "Failed to allocate default application id."); for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) { hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0); - ExitOnFailure(hr, "Failed to allocate default application type."); + ApupExitOnFailure(hr, "Failed to allocate default application type."); } } } @@ -79,13 +93,13 @@ extern "C" HRESULT DAPI ApupAllocChainFromAtom( if (pFeed->cEntries) { pChain->rgEntries = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE)); - ExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries."); + ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries."); // Process each entry, building up the chain. for (DWORD i = 0; i < pFeed->cEntries; ++i) { hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries); - ExitOnFailure(hr, "Failed to process ATOM entry."); + ApupExitOnFailure(hr, "Failed to process ATOM entry."); if (S_FALSE != hr) { @@ -103,7 +117,7 @@ extern "C" HRESULT DAPI ApupAllocChainFromAtom( if (pChain->cEntries > 0) { pChain->rgEntries = static_cast(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE)); - ExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries."); + ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries."); } else { @@ -136,21 +150,21 @@ HRESULT DAPI ApupFilterChain( DWORD cEntries = NULL; pNewChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); - ExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); + ApupExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); hr = FilterEntries(pChain->rgEntries, pChain->cEntries, pVersion, &prgEntries, &cEntries); - ExitOnFailure(hr, "Failed to filter entries by version."); + ApupExitOnFailure(hr, "Failed to filter entries by version."); if (pChain->wzDefaultApplicationId) { hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0); - ExitOnFailure(hr, "Failed to copy default application id."); + ApupExitOnFailure(hr, "Failed to copy default application id."); } if (pChain->wzDefaultApplicationType) { hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0); - ExitOnFailure(hr, "Failed to copy default application type."); + ApupExitOnFailure(hr, "Failed to copy default application type."); } pNewChain->rgEntries = prgEntries; @@ -205,28 +219,28 @@ static HRESULT ProcessEntry( if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) { hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0); - ExitOnFailure(hr, "Failed to allocate application identity."); + ApupExitOnFailure(hr, "Failed to allocate application identity."); for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) { hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0); - ExitOnFailure(hr, "Failed to allocate application type."); + ApupExitOnFailure(hr, "Failed to allocate application type."); } } } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1)) { hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0); - ExitOnFailure(hr, "Failed to allocate upgrade id."); + ApupExitOnFailure(hr, "Failed to allocate upgrade id."); for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1)) { hr = VerParseVersion(pAttribute->wzValue, 0, FALSE, &pApupEntry->pUpgradeVersion); - ExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue); + ApupExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1)) { @@ -240,7 +254,7 @@ static HRESULT ProcessEntry( else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1)) { hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion); - ExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); + ApupExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); fVersionFound = TRUE; } @@ -254,24 +268,24 @@ static HRESULT ProcessEntry( } hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare version to upgrade version."); + ApupExitOnFailure(hr, "Failed to compare version to upgrade version."); if (nCompareResult >= 0) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); + ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); } if (pAtomEntry->wzTitle) { hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0); - ExitOnFailure(hr, "Failed to allocate application title."); + ApupExitOnFailure(hr, "Failed to allocate application title."); } if (pAtomEntry->wzSummary) { hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0); - ExitOnFailure(hr, "Failed to allocate application summary."); + ApupExitOnFailure(hr, "Failed to allocate application summary."); } if (pAtomEntry->pContent) @@ -279,18 +293,18 @@ static HRESULT ProcessEntry( if (pAtomEntry->pContent->wzType) { hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0); - ExitOnFailure(hr, "Failed to allocate content type."); + ApupExitOnFailure(hr, "Failed to allocate content type."); } if (pAtomEntry->pContent->wzValue) { hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0); - ExitOnFailure(hr, "Failed to allocate content."); + ApupExitOnFailure(hr, "Failed to allocate content."); } } // Now process the enclosures. Assume every link in the ATOM entry is an enclosure. pApupEntry->rgEnclosures = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE)); - ExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry."); + ApupExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry."); for (DWORD i = 0; i < pAtomEntry->cLinks; ++i) { @@ -298,7 +312,7 @@ static HRESULT ProcessEntry( if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1)) { hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures); - ExitOnFailure(hr, "Failed to parse enclosure."); + ApupExitOnFailure(hr, "Failed to parse enclosure."); pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures @@ -369,25 +383,25 @@ static HRESULT ParseEnclosure( dwDigestStringLength = 2 * dwDigestLength; hr = ::StringCchLengthW(pElement->wzValue, STRSAFE_MAX_CCH, &cchDigestString); - ExitOnFailure(hr, "Failed to get string length of digest value."); + ApupExitOnFailure(hr, "Failed to get string length of digest value."); if (dwDigestStringLength != cchDigestString) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Invalid digest length (%zu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); + ApupExitOnRootFailure(hr, "Invalid digest length (%zu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); } pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength; pEnclosure->rgbDigest = static_cast(MemAlloc(pEnclosure->cbDigest, TRUE)); - ExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); + ApupExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest); - ExitOnFailure(hr, "Failed to decode digest value."); + ApupExitOnFailure(hr, "Failed to decode digest value."); } else { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Unknown algorithm type for digest."); + ApupExitOnRootFailure(hr, "Unknown algorithm type for digest."); } break; @@ -395,7 +409,7 @@ static HRESULT ParseEnclosure( else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1)) { hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0); - ExitOnFailure(hr, "Failed to copy local name."); + ApupExitOnFailure(hr, "Failed to copy local name."); } } } @@ -403,7 +417,7 @@ static HRESULT ParseEnclosure( pEnclosure->dw64Size = pLink->dw64Length; hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0); - ExitOnFailure(hr, "Failed to allocate enclosure URL."); + ApupExitOnFailure(hr, "Failed to allocate enclosure URL."); pEnclosure->fInstaller = FALSE; pEnclosure->wzLocalName = NULL; @@ -459,7 +473,7 @@ static HRESULT FilterEntries( const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i; hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare versions."); + ApupExitOnFailure(hr, "Failed to compare versions."); if (nCompareResult >= 0) { @@ -467,7 +481,7 @@ static HRESULT FilterEntries( } hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pUpgradeVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare upgrade versions."); + ApupExitOnFailure(hr, "Failed to compare upgrade versions."); if (nCompareResult > 0 || (!pEntry->fUpgradeExclusive && nCompareResult == 0)) { @@ -481,17 +495,17 @@ static HRESULT FilterEntries( DWORD cNewFilteredEntries = *pcFilteredEntries + 1; hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries); + ApupExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries); if (*prgFilteredEntries) { pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries."); + ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries."); } else { pv = MemAlloc(cbAllocSize, TRUE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries."); + ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries."); } *pcFilteredEntries = cNewFilteredEntries; @@ -499,10 +513,10 @@ static HRESULT FilterEntries( pv = NULL; hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1); - ExitOnFailure(hr, "Failed to deep copy entry."); + ApupExitOnFailure(hr, "Failed to deep copy entry."); hr = VerCompareParsedVersions(pRequired->pVersion, rgEntries[0].pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare required version."); + ApupExitOnFailure(hr, "Failed to compare required version."); if (nCompareResult < 0) { @@ -530,67 +544,67 @@ static HRESULT CopyEntry( if (pSrc->wzApplicationId) { hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0); - ExitOnFailure(hr, "Failed to copy application id."); + ApupExitOnFailure(hr, "Failed to copy application id."); } if (pSrc->wzApplicationType) { hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0); - ExitOnFailure(hr, "Failed to copy application type."); + ApupExitOnFailure(hr, "Failed to copy application type."); } if (pSrc->wzUpgradeId) { hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0); - ExitOnFailure(hr, "Failed to copy upgrade id."); + ApupExitOnFailure(hr, "Failed to copy upgrade id."); } if (pSrc->wzTitle) { hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0); - ExitOnFailure(hr, "Failed to copy title."); + ApupExitOnFailure(hr, "Failed to copy title."); } if (pSrc->wzSummary) { hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0); - ExitOnFailure(hr, "Failed to copy summary."); + ApupExitOnFailure(hr, "Failed to copy summary."); } if (pSrc->wzContentType) { hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0); - ExitOnFailure(hr, "Failed to copy content type."); + ApupExitOnFailure(hr, "Failed to copy content type."); } if (pSrc->wzContent) { hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0); - ExitOnFailure(hr, "Failed to copy content."); + ApupExitOnFailure(hr, "Failed to copy content."); } pDest->dw64TotalSize = pSrc->dw64TotalSize; hr = VerCopyVersion(pSrc->pUpgradeVersion, &pDest->pUpgradeVersion); - ExitOnFailure(hr, "Failed to copy upgrade version."); + ApupExitOnFailure(hr, "Failed to copy upgrade version."); hr = VerCopyVersion(pSrc->pVersion, &pDest->pVersion); - ExitOnFailure(hr, "Failed to copy version."); + ApupExitOnFailure(hr, "Failed to copy version."); pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive; hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize); - ExitOnRootFailure(hr, "Overflow while calculating memory allocation size"); + ApupExitOnRootFailure(hr, "Overflow while calculating memory allocation size"); pDest->rgEnclosures = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures."); + ApupExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures."); pDest->cEnclosures = pSrc->cEnclosures; for (DWORD i = 0; i < pDest->cEnclosures; ++i) { hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i); - ExitOnFailure(hr, "Failed to copy enclosure."); + ApupExitOnFailure(hr, "Failed to copy enclosure."); } LExit: @@ -615,17 +629,17 @@ static HRESULT CopyEnclosure( if (pSrc->wzUrl) { hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0); - ExitOnFailure(hr, "Failed copy url."); + ApupExitOnFailure(hr, "Failed copy url."); } if (pSrc->wzLocalName) { hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0); - ExitOnFailure(hr, "Failed copy url."); + ApupExitOnFailure(hr, "Failed copy url."); } pDest->rgbDigest = static_cast(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE)); - ExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest."); + ApupExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest."); pDest->cbDigest = pSrc->cbDigest; diff --git a/src/dutil/atomutil.cpp b/src/dutil/atomutil.cpp index 4a12fb80..c7c7975a 100644 --- a/src/dutil/atomutil.cpp +++ b/src/dutil/atomutil.cpp @@ -2,6 +2,19 @@ #include "precomp.h" +// Exit macros +#define AtomExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) +#define AtomExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) +#define AtomExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) +#define AtomExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) +#define AtomExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ATOMUTIL, e, x, s, __VA_ARGS__) +#define AtomExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ATOMUTIL, g, x, s, __VA_ARGS__) static HRESULT ParseAtomDocument( __in IXMLDOMDocument *pixd, @@ -98,7 +111,7 @@ extern "C" void DAPI AtomUninitialize() *********************************************************************/ extern "C" HRESULT DAPI AtomParseFromString( - __in LPCWSTR wzAtomString, + __in_z LPCWSTR wzAtomString, __out ATOM_FEED **ppFeed ) { @@ -110,10 +123,10 @@ extern "C" HRESULT DAPI AtomParseFromString( IXMLDOMDocument *pixdAtom = NULL; hr = XmlLoadDocument(wzAtomString, &pixdAtom); - ExitOnFailure(hr, "Failed to load ATOM string as XML document."); + AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); hr = ParseAtomDocument(pixdAtom, &pNewFeed); - ExitOnFailure(hr, "Failed to parse ATOM document."); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); *ppFeed = pNewFeed; pNewFeed = NULL; @@ -131,7 +144,7 @@ LExit: *********************************************************************/ extern "C" HRESULT DAPI AtomParseFromFile( - __in LPCWSTR wzAtomFile, + __in_z LPCWSTR wzAtomFile, __out ATOM_FEED **ppFeed ) { @@ -143,10 +156,10 @@ extern "C" HRESULT DAPI AtomParseFromFile( IXMLDOMDocument *pixdAtom = NULL; hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom); - ExitOnFailure(hr, "Failed to load ATOM string as XML document."); + AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); hr = ParseAtomDocument(pixdAtom, &pNewFeed); - ExitOnFailure(hr, "Failed to parse ATOM document."); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); *ppFeed = pNewFeed; pNewFeed = NULL; @@ -175,7 +188,7 @@ extern "C" HRESULT DAPI AtomParseFromDocument( ATOM_FEED *pNewFeed = NULL; hr = ParseAtomDocument(pixdDocument, &pNewFeed); - ExitOnFailure(hr, "Failed to parse ATOM document."); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); *ppFeed = pNewFeed; pNewFeed = NULL; @@ -192,7 +205,7 @@ LExit: *********************************************************************/ extern "C" void DAPI AtomFreeFeed( - __in_xcount(pFeed->cItems) ATOM_FEED *pFeed + __in_xcount(pFeed->cItems) ATOM_FEED* pFeed ) { if (pFeed) @@ -257,10 +270,10 @@ static HRESULT ParseAtomDocument( // Get the document element and start processing feeds. // hr = pixd->get_documentElement(&pFeedElement); - ExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); + AtomExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); hr = ParseAtomFeed(pFeedElement, &pNewFeed); - ExitOnFailure(hr, "Failed to parse ATOM feed."); + AtomExitOnFailure(hr, "Failed to parse ATOM feed."); if (S_FALSE == hr) { @@ -305,96 +318,96 @@ static HRESULT ParseAtomFeed( // First, allocate the new feed and all the possible sub elements. pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE); - ExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); + AtomExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); pNewFeed->pixn = pixnFeed; pNewFeed->pixn->AddRef(); hr = AllocateAtomType(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors); - ExitOnFailure(hr, "Failed to allocate ATOM feed authors."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed authors."); hr = AllocateAtomType(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories); - ExitOnFailure(hr, "Failed to allocate ATOM feed categories."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed categories."); hr = AllocateAtomType(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries); - ExitOnFailure(hr, "Failed to allocate ATOM feed entries."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed entries."); hr = AllocateAtomType(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks); - ExitOnFailure(hr, "Failed to allocate ATOM feed links."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed links."); // Second, process the elements under a feed. hr = pixnFeed->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1)) { hr = AssignString(&pNewFeed->wzGenerator, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM feed generator."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed generator."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1)) { hr = AssignString(&pNewFeed->wzIcon, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM feed icon."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed icon."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) { hr = AssignString(&pNewFeed->wzId, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM feed id."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed id."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1)) { hr = AssignString(&pNewFeed->wzLogo, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM feed logo."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed logo."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1)) { hr = AssignString(&pNewFeed->wzSubtitle, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) { hr = AssignString(&pNewFeed->wzTitle, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM feed title."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed title."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) { hr = AssignDateTime(&pNewFeed->ftUpdated, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM feed updated."); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed updated."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) { hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]); - ExitOnFailure(hr, "Failed to parse ATOM author."); + AtomExitOnFailure(hr, "Failed to parse ATOM author."); ++cAuthors; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) { hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]); - ExitOnFailure(hr, "Failed to parse ATOM category."); + AtomExitOnFailure(hr, "Failed to parse ATOM category."); ++cCategories; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1)) { hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]); - ExitOnFailure(hr, "Failed to parse ATOM entry."); + AtomExitOnFailure(hr, "Failed to parse ATOM entry."); ++cEntries; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) { hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]); - ExitOnFailure(hr, "Failed to parse ATOM link."); + AtomExitOnFailure(hr, "Failed to parse ATOM link."); ++cLinks; } else { hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements); - ExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeName); @@ -404,17 +417,17 @@ static HRESULT ParseAtomFeed( if (!pNewFeed->wzId || !*pNewFeed->wzId) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find required feed/id element."); + AtomExitOnRootFailure(hr, "Failed to find required feed/id element."); } else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find required feed/title element."); + AtomExitOnRootFailure(hr, "Failed to find required feed/title element."); } else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find required feed/updated element."); + AtomExitOnRootFailure(hr, "Failed to find required feed/updated element."); } *ppFeed = pNewFeed; @@ -450,12 +463,12 @@ template static HRESULT AllocateAtomType( T* prgT = NULL; hr = XmlSelectNodes(pixnParent, wzT, &pNodeList); - ExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); + AtomExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); if (S_OK == hr) { hr = pNodeList->get_length(&cT); - ExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); + AtomExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); if (cT == 0) { @@ -463,7 +476,7 @@ template static HRESULT AllocateAtomType( } prgT = static_cast(MemAlloc(sizeof(T) * cT, TRUE)); - ExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); + AtomExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); *pcT = cT; *pprgT = prgT; @@ -499,30 +512,30 @@ static HRESULT ParseAtomAuthor( BSTR bstrNodeName = NULL; hr = pixnAuthor->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1)) { hr = AssignString(&pAuthor->wzName, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM author name."); + AtomExitOnFailure(hr, "Failed to allocate ATOM author name."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1)) { hr = AssignString(&pAuthor->wzEmail, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM author email."); + AtomExitOnFailure(hr, "Failed to allocate ATOM author email."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1)) { hr = AssignString(&pAuthor->wzUrl, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM author uri."); + AtomExitOnFailure(hr, "Failed to allocate ATOM author uri."); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM author elements."); + AtomExitOnFailure(hr, "Failed to process all ATOM author elements."); hr = S_OK; @@ -553,44 +566,44 @@ static HRESULT ParseAtomCategory( // Process attributes first. hr = pixnCategory->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1)) { hr = AssignString(&pCategory->wzLabel, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM category label."); + AtomExitOnFailure(hr, "Failed to allocate ATOM category label."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1)) { hr = AssignString(&pCategory->wzScheme, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM category scheme."); + AtomExitOnFailure(hr, "Failed to allocate ATOM category scheme."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1)) { hr = AssignString(&pCategory->wzTerm, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM category term."); + AtomExitOnFailure(hr, "Failed to allocate ATOM category term."); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM category attributes."); + AtomExitOnFailure(hr, "Failed to process all ATOM category attributes."); // Process elements second. hr = pixnCategory->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements); - ExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM category elements."); + AtomExitOnFailure(hr, "Failed to process all ATOM category elements."); hr = S_OK; @@ -622,42 +635,42 @@ static HRESULT ParseAtomContent( // Process attributes first. hr = pixnContent->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) { hr = AssignString(&pContent->wzType, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM content type."); + AtomExitOnFailure(hr, "Failed to allocate ATOM content type."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1)) { hr = AssignString(&pContent->wzUrl, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM content scheme."); + AtomExitOnFailure(hr, "Failed to allocate ATOM content scheme."); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM content attributes."); + AtomExitOnFailure(hr, "Failed to process all ATOM content attributes."); // Process elements second. hr = pixnContent->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements); - ExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM content elements."); + AtomExitOnFailure(hr, "Failed to process all ATOM content elements."); hr = AssignString(&pContent->wzValue, pixnContent); - ExitOnFailure(hr, "Failed to allocate ATOM content value."); + AtomExitOnFailure(hr, "Failed to allocate ATOM content value."); LExit: ReleaseBSTR(bstrNodeName); @@ -694,56 +707,56 @@ static HRESULT ParseAtomEntry( // First, allocate all the possible sub elements. hr = AllocateAtomType(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors); - ExitOnFailure(hr, "Failed to allocate ATOM entry authors."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry authors."); hr = AllocateAtomType(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories); - ExitOnFailure(hr, "Failed to allocate ATOM entry categories."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry categories."); hr = AllocateAtomType(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks); - ExitOnFailure(hr, "Failed to allocate ATOM entry links."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry links."); // Second, process elements. hr = pixnEntry->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) { hr = AssignString(&pEntry->wzId, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM entry id."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry id."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1)) { hr = AssignString(&pEntry->wzSummary, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM entry summary."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry summary."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) { hr = AssignString(&pEntry->wzTitle, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM entry title."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry title."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1)) { hr = AssignDateTime(&pEntry->ftPublished, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM entry published."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry published."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) { hr = AssignDateTime(&pEntry->ftUpdated, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM entry updated."); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry updated."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) { hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]); - ExitOnFailure(hr, "Failed to parse ATOM entry author."); + AtomExitOnFailure(hr, "Failed to parse ATOM entry author."); ++cAuthors; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) { hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]); - ExitOnFailure(hr, "Failed to parse ATOM entry category."); + AtomExitOnFailure(hr, "Failed to parse ATOM entry category."); ++cCategories; } @@ -752,47 +765,47 @@ static HRESULT ParseAtomEntry( if (NULL != pEntry->pContent) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); + AtomExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); } pEntry->pContent = static_cast(MemAlloc(sizeof(ATOM_CONTENT), TRUE)); - ExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); + AtomExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); hr = ParseAtomContent(pNode, pEntry->pContent); - ExitOnFailure(hr, "Failed to parse ATOM entry content."); + AtomExitOnFailure(hr, "Failed to parse ATOM entry content."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) { hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]); - ExitOnFailure(hr, "Failed to parse ATOM entry link."); + AtomExitOnFailure(hr, "Failed to parse ATOM entry link."); ++cLinks; } else { hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements); - ExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM entry elements."); + AtomExitOnFailure(hr, "Failed to process all ATOM entry elements."); if (!pEntry->wzId || !*pEntry->wzId) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); } else if (!pEntry->wzTitle || !*pEntry->wzTitle) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); } else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); } hr = S_OK; @@ -825,19 +838,19 @@ static HRESULT ParseAtomLink( // Process attributes first. hr = pixnLink->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "Failed get attributes for ATOM link."); + AtomExitOnFailure(hr, "Failed get attributes for ATOM link."); while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1)) { hr = AssignString(&pLink->wzRel, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM link rel."); + AtomExitOnFailure(hr, "Failed to allocate ATOM link rel."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1)) { hr = AssignString(&pLink->wzUrl, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM link href."); + AtomExitOnFailure(hr, "Failed to allocate ATOM link href."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1)) { @@ -846,45 +859,45 @@ static HRESULT ParseAtomLink( { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } - ExitOnFailure(hr, "Failed to parse ATOM link length."); + AtomExitOnFailure(hr, "Failed to parse ATOM link length."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) { hr = AssignString(&pLink->wzTitle, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM link title."); + AtomExitOnFailure(hr, "Failed to allocate ATOM link title."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) { hr = AssignString(&pLink->wzType, pNode); - ExitOnFailure(hr, "Failed to allocate ATOM link type."); + AtomExitOnFailure(hr, "Failed to allocate ATOM link type."); } else { hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes); - ExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM link attributes."); + AtomExitOnFailure(hr, "Failed to process all ATOM link attributes."); // Process elements second. hr = pixnLink->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements); - ExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } - ExitOnFailure(hr, "Failed to process all ATOM link elements."); + AtomExitOnFailure(hr, "Failed to process all ATOM link elements."); hr = AssignString(&pLink->wzValue, pixnLink); - ExitOnFailure(hr, "Failed to allocate ATOM link value."); + AtomExitOnFailure(hr, "Failed to allocate ATOM link value."); LExit: ReleaseBSTR(bstrNodeName); @@ -916,39 +929,39 @@ static HRESULT ParseAtomUnknownElement( ATOM_UNKNOWN_ELEMENT* pNewUnknownElement; pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE); - ExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + AtomExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); - ExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); } else if (S_FALSE == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get unknown element namespace."); + AtomExitOnFailure(hr, "Failed to get unknown element namespace."); hr = pNode->get_baseName(&bstrNodeName); - ExitOnFailure(hr, "Failed to get unknown element name."); + AtomExitOnFailure(hr, "Failed to get unknown element name."); hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); - ExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get unknown element value."); + AtomExitOnFailure(hr, "Failed to get unknown element value."); hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); hr = pNode->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) { hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); - ExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); + AtomExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); ReleaseNullObject(pixnAttribute); } @@ -957,7 +970,7 @@ static HRESULT ParseAtomUnknownElement( { hr = S_OK; } - ExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); + AtomExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; while (*ppTail) @@ -999,31 +1012,31 @@ static HRESULT ParseAtomUnknownAttribute( ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE); - ExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + AtomExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); - ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); } else if (S_FALSE == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get unknown attribute namespace."); + AtomExitOnFailure(hr, "Failed to get unknown attribute namespace."); hr = pNode->get_baseName(&bstrNodeName); - ExitOnFailure(hr, "Failed to get unknown attribute name."); + AtomExitOnFailure(hr, "Failed to get unknown attribute name."); hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); - ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get unknown attribute value."); + AtomExitOnFailure(hr, "Failed to get unknown attribute value."); hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; while (*ppTail) @@ -1060,16 +1073,16 @@ static HRESULT AssignDateTime( if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Already process this datetime value."); + AtomExitOnRootFailure(hr, "Already process this datetime value."); } hr = XmlGetText(pNode, &bstrValue); - ExitOnFailure(hr, "Failed to get value."); + AtomExitOnFailure(hr, "Failed to get value."); if (S_OK == hr) { hr = TimeFromString3339(bstrValue, pft); - ExitOnFailure(hr, "Failed to convert value to time."); + AtomExitOnFailure(hr, "Failed to convert value to time."); } else { @@ -1099,16 +1112,16 @@ static HRESULT AssignString( if (pwzValue && *pwzValue) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Already processed this value."); + AtomExitOnRootFailure(hr, "Already processed this value."); } hr = XmlGetText(pNode, &bstrValue); - ExitOnFailure(hr, "Failed to get value."); + AtomExitOnFailure(hr, "Failed to get value."); if (S_OK == hr) { hr = StrAllocString(pwzValue, bstrValue, 0); - ExitOnFailure(hr, "Failed to allocate value."); + AtomExitOnFailure(hr, "Failed to allocate value."); } else { diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp index a70aaa81..a6d3ac90 100644 --- a/src/dutil/buffutil.cpp +++ b/src/dutil/buffutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define BuffExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) +#define BuffExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) +#define BuffExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) +#define BuffExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) +#define BuffExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUFFUTIL, e, x, s, __VA_ARGS__) +#define BuffExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_BUFFUTIL, g, x, s, __VA_ARGS__) + + // constants #define BUFFER_INCREMENT 128 @@ -11,7 +26,7 @@ // helper function declarations static HRESULT EnsureBufferSize( - __deref_out_bcount(cbSize) BYTE** ppbBuffer, + __deref_inout_bcount(cbSize) BYTE** ppbBuffer, __in SIZE_T cbSize ); @@ -34,13 +49,13 @@ extern "C" HRESULT BuffReadNumber( // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); // verify buffer size if (sizeof(DWORD) > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small."); + BuffExitOnRootFailure(hr, "Buffer too small."); } *pdw = *(const DWORD*)(pbBuffer + *piBuffer); @@ -66,13 +81,13 @@ extern "C" HRESULT BuffReadNumber64( // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); // verify buffer size if (sizeof(DWORD64) > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small."); + BuffExitOnRootFailure(hr, "Buffer too small."); } *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer); @@ -98,13 +113,13 @@ extern "C" HRESULT BuffReadPointer( // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); // verify buffer size if (sizeof(DWORD_PTR) > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small."); + BuffExitOnRootFailure(hr, "Buffer too small."); } *pdw64 = *(const DWORD_PTR*)(pbBuffer + *piBuffer); @@ -132,38 +147,38 @@ extern "C" HRESULT BuffReadString( // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); // verify buffer size if (sizeof(DWORD) > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small."); + BuffExitOnRootFailure(hr, "Buffer too small."); } // read character count cch = *(const DWORD*)(pbBuffer + *piBuffer); hr = ::DWordMult(cch, static_cast(sizeof(WCHAR)), &cb); - ExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer); - ExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); // verify buffer size if (cb > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small to hold character data."); + BuffExitOnRootFailure(hr, "Buffer too small to hold character data."); } // copy character data hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch); - ExitOnFailure(hr, "Failed to copy character data."); + BuffExitOnFailure(hr, "Failed to copy character data."); *piBuffer += cb; @@ -189,38 +204,38 @@ extern "C" HRESULT BuffReadStringAnsi( // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); // verify buffer size if (sizeof(DWORD) > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small."); + BuffExitOnRootFailure(hr, "Buffer too small."); } // read character count cch = *(const DWORD*)(pbBuffer + *piBuffer); hr = ::DWordMult(cch, static_cast(sizeof(CHAR)), &cb); - ExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer); - ExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); // verify buffer size if (cb > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small to hold character count."); + BuffExitOnRootFailure(hr, "Buffer too small to hold character count."); } // copy character data hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch); - ExitOnFailure(hr, "Failed to copy character data."); + BuffExitOnFailure(hr, "Failed to copy character data."); *piBuffer += cb; @@ -232,7 +247,7 @@ extern "C" HRESULT BuffReadStream( __in_bcount(cbBuffer) const BYTE* pbBuffer, __in SIZE_T cbBuffer, __inout SIZE_T* piBuffer, - __deref_out_bcount(*pcbStream) BYTE** ppbStream, + __deref_inout_bcount(*pcbStream) BYTE** ppbStream, __out SIZE_T* pcbStream ) { @@ -247,13 +262,13 @@ extern "C" HRESULT BuffReadStream( // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); // verify buffer size if (sizeof(DWORD64) > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small."); + BuffExitOnRootFailure(hr, "Buffer too small."); } // read stream size @@ -262,18 +277,18 @@ extern "C" HRESULT BuffReadStream( // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - ExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer."); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer."); // verify buffer size if (cb > cbAvailable) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Buffer too small to hold byte count."); + BuffExitOnRootFailure(hr, "Buffer too small to hold byte count."); } // allocate buffer *ppbStream = (BYTE*)MemAlloc((SIZE_T)cb, TRUE); - ExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); + BuffExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); // read stream data memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, (SIZE_T)cb); @@ -287,7 +302,7 @@ LExit: } extern "C" HRESULT BuffWriteNumber( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in DWORD_PTR dw ) @@ -299,7 +314,7 @@ extern "C" HRESULT BuffWriteNumber( // make sure we have a buffer with sufficient space hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD)); - ExitOnFailure(hr, "Failed to ensure buffer size."); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy data to buffer *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; @@ -310,7 +325,7 @@ LExit: } extern "C" HRESULT BuffWriteNumber64( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in DWORD64 dw64 ) @@ -322,7 +337,7 @@ extern "C" HRESULT BuffWriteNumber64( // make sure we have a buffer with sufficient space hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64)); - ExitOnFailure(hr, "Failed to ensure buffer size."); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy data to buffer *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64; @@ -333,7 +348,7 @@ LExit: } extern "C" HRESULT BuffWritePointer( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in DWORD_PTR dw ) @@ -345,7 +360,7 @@ extern "C" HRESULT BuffWritePointer( // make sure we have a buffer with sufficient space hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD_PTR)); - ExitOnFailure(hr, "Failed to ensure buffer size."); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy data to buffer *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; @@ -356,7 +371,7 @@ LExit: } extern "C" HRESULT BuffWriteString( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in_z_opt LPCWSTR scz ) @@ -370,7 +385,7 @@ extern "C" HRESULT BuffWriteString( // make sure we have a buffer with sufficient space hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb)); - ExitOnFailure(hr, "Failed to ensure buffer size."); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy character count to buffer *(DWORD*)(*ppbBuffer + *piBuffer) = cch; @@ -385,7 +400,7 @@ LExit: } extern "C" HRESULT BuffWriteStringAnsi( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in_z_opt LPCSTR scz ) @@ -399,7 +414,7 @@ extern "C" HRESULT BuffWriteStringAnsi( // make sure we have a buffer with sufficient space hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb)); - ExitOnFailure(hr, "Failed to ensure buffer size."); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy character count to buffer *(DWORD*)(*ppbBuffer + *piBuffer) = cch; @@ -414,7 +429,7 @@ LExit: } extern "C" HRESULT BuffWriteStream( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in_bcount(cbStream) const BYTE* pbStream, __in SIZE_T cbStream @@ -429,7 +444,7 @@ extern "C" HRESULT BuffWriteStream( // make sure we have a buffer with sufficient space hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(DWORD64)); - ExitOnFailure(hr, "Failed to ensure buffer size."); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy byte count to buffer *(DWORD64*)(*ppbBuffer + *piBuffer) = cb; @@ -447,7 +462,7 @@ LExit: // helper functions static HRESULT EnsureBufferSize( - __deref_out_bcount(cbSize) BYTE** ppbBuffer, + __deref_inout_bcount(cbSize) BYTE** ppbBuffer, __in SIZE_T cbSize ) { @@ -459,14 +474,14 @@ static HRESULT EnsureBufferSize( if (MemSize(*ppbBuffer) < cbTarget) { LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer."); + BuffExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer."); *ppbBuffer = (BYTE*)pv; } } else { *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE); - ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); + BuffExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); } LExit: diff --git a/src/dutil/cabcutil.cpp b/src/dutil/cabcutil.cpp index 8619822d..93a9b7e1 100644 --- a/src/dutil/cabcutil.cpp +++ b/src/dutil/cabcutil.cpp @@ -2,6 +2,22 @@ #include "precomp.h" + +// Exit macros +#define CabcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) +#define CabcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) +#define CabcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) +#define CabcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) +#define CabcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABCUTIL, e, x, s, __VA_ARGS__) +#define CabcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABCUTIL, g, x, s, __VA_ARGS__) + + static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?'; static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024; @@ -144,19 +160,19 @@ static HRESULT UtcFileTimeToLocalDosDateTime( __out USHORT* pTime ); -static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __inout_bcount(CABC_HANDLE_BYTES) void *pv); static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb); static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback INT_PTR DIAMONDAPI CabCOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback UINT FAR DIAMONDAPI CabCRead(__in INT_PTR hf, __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback UINT FAR DIAMONDAPI CabCWrite(__in INT_PTR hf, __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback long FAR DIAMONDAPI CabCSeek(__in INT_PTR hf, __in long dist, __in int seektype, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); -__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __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); +static __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); +static __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); +static __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); +static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __inout_bcount(CABC_HANDLE_BYTES) void *pv); __success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv); static __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); -static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __inout_bcount(CABC_HANDLE_BYTES) void *pv); /******************************************************************** @@ -174,7 +190,7 @@ extern "C" HRESULT DAPI CabCBegin( __in DWORD dwMaxSize, __in DWORD dwMaxThresh, __in COMPRESSION_TYPE ct, - __out HANDLE *phContext + __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext ) { Assert(wzCab && *wzCab && phContext); @@ -190,28 +206,28 @@ extern "C" HRESULT DAPI CabCBegin( if (wzCabDir) { hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); - ExitOnFailure(hr, "Failed to get length of cab directory"); + CabcExitOnFailure(hr, "Failed to get length of cab directory"); // Need room to terminate with L'\\' and L'\0' if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); + CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); } hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir); - ExitOnFailure(hr, "Failed to copy cab directory to buffer"); + CabcExitOnFailure(hr, "Failed to copy cab directory to buffer"); if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) { hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\"); - ExitOnFailure(hr, "Failed to cat \\ to end of buffer"); + CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer"); ++cchPathBuffer; } } pcd = static_cast(MemAlloc(sizeof(CABC_DATA), TRUE)); - ExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure"); + CabcExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure"); pcd->hrLastError = S_OK; pcd->fGoodCab = TRUE; @@ -266,35 +282,35 @@ extern "C" HRESULT DAPI CabCBegin( else { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid compression type specified."); + CabcExitOnFailure(hr, "Invalid compression type specified."); } if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL)) { - ExitWithLastError(hr, "failed to convert cab name to multi-byte"); + CabcExitWithLastError(hr, "failed to convert cab name to multi-byte"); } if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) { - ExitWithLastError(hr, "failed to convert cab dir to multi-byte"); + CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte"); } // Remember the path to the cabinet. hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); - ExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); + CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab); - ExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); + CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); // Get the empty file to use as the blank marker for duplicates. if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) { - ExitWithLastError(hr, "Failed to get temp path."); + CabcExitWithLastError(hr, "Failed to get temp path."); } if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile)) { - ExitWithLastError(hr, "Failed to create a temp file name."); + CabcExitWithLastError(hr, "Failed to create a temp file name."); } // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) @@ -303,7 +319,7 @@ extern "C" HRESULT DAPI CabCBegin( 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); hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); + CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files if (1 > dwMaxFiles) @@ -315,10 +331,10 @@ extern "C" HRESULT DAPI CabCBegin( size_t cbFileAllocSize = 0; hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize)); - ExitOnFailure(hr, "Maximum allocation exceeded on initialization."); + CabcExitOnFailure(hr, "Maximum allocation exceeded on initialization."); pcd->prgFiles = static_cast(MemAlloc(cbFileAllocSize, TRUE)); - ExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files."); + CabcExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files."); // Tell cabinet API about our configuration. pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd); @@ -331,12 +347,12 @@ extern "C" HRESULT DAPI CabCBegin( } else { - ExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + CabcExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); } pcd->fGoodCab = FALSE; - ExitOnFailure(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? + 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? } *phContext = pcd; @@ -392,25 +408,25 @@ extern "C" HRESULT DAPI CabCAddFile( { // Store file size, primarily used to determine which files to hash for duplicates hr = FileSize(wzFile, &llFileSize); - ExitOnFailure(hr, "Failed to check size of file %ls", wzFile); + CabcExitOnFailure(hr, "Failed to check size of file %ls", wzFile); hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize); - ExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); + CabcExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); } if (pcfDuplicate) // This will be null for smart cabbing case { DWORD index; hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index); - ExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); + CabcExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex); - ExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); + CabcExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); } else { hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); - ExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); + CabcExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); } ++pcd->dwLastFileIndex; @@ -483,13 +499,13 @@ extern "C" HRESULT DAPI CabCFinish( { LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken; hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - ExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp); + CabcExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp); } else { LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - ExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp); + CabcExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp); } if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates) @@ -518,13 +534,13 @@ extern "C" HRESULT DAPI CabCFinish( { LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken; hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - ExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp); + CabcExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp); } else { LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - ExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp); + CabcExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp); } // 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 @@ -543,14 +559,14 @@ extern "C" HRESULT DAPI CabCFinish( else // If it's neither duplicate nor non-duplicate, throw an error { hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); - ExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered"); + CabcExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered"); } if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) { if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) { - ExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + CabcExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); } pcd->llBytesSinceLastFlush = 0; } @@ -574,10 +590,10 @@ extern "C" HRESULT DAPI CabCFinish( } else { - ExitWithLastError(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); + 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); } - ExitOnFailure(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? + 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? } // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet @@ -585,14 +601,14 @@ extern "C" HRESULT DAPI CabCFinish( if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError)) { hr = pcd->hrLastError; - ExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet."); + CabcExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet."); } if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) { if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) { - ExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + CabcExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); } pcd->llBytesSinceLastFlush = 0; } @@ -610,10 +626,10 @@ extern "C" HRESULT DAPI CabCFinish( } else { - ExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %s", pcd->erf.erfOper, pcd->erf.erfType); + CabcExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); } - ExitOnFailure(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %s", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS? + 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? } // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs) @@ -621,13 +637,13 @@ extern "C" HRESULT DAPI CabCFinish( { // If we have a last error, use that, otherwise return the useless error hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL; - ExitOnFailure(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? + 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? } if (pcd->fGoodCab && pcd->cDuplicates) { hr = UpdateDuplicateFiles(pcd); - ExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); + CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); } LExit: @@ -697,8 +713,8 @@ static HRESULT CheckForDuplicateFile( HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; - ExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file"); - ExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file"); + CabcExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file"); + CabcExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file"); *ppcf = NULL; // By default, we'll set our output to NULL @@ -712,7 +728,7 @@ static HRESULT CheckForDuplicateFile( { hr = S_OK; } - ExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files"); + CabcExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files"); for (i = 0; i < pcd->cFilePaths; ++i) { @@ -723,22 +739,22 @@ static HRESULT CheckForDuplicateFile( if (pcd->prgFiles[i].pmfHash == NULL) { pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - ExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash"); + CabcExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash"); pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash); - ExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath); + CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath); } // If our own file hasn't yet been hashed, hash it if (NULL == *ppmfHash) { *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - ExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash"); + CabcExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash"); (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash); - ExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath); + CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath); } // If the two file hashes are both of the expected size, and they match, we've got a match, so return it! @@ -779,17 +795,17 @@ static HRESULT AddDuplicateFile( size_t cbDuplicates = 0; hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates); - ExitOnFailure(hr, "Maximum allocation exceeded."); + CabcExitOnFailure(hr, "Maximum allocation exceeded."); if (pcd->cDuplicates) { pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file."); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file."); } else { pv = MemAlloc(cbDuplicates, FALSE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file."); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file."); } ZeroMemory(reinterpret_cast(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE)); @@ -804,12 +820,12 @@ static HRESULT AddDuplicateFile( pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0); - ExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath); + CabcExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath); if (wzToken && *wzToken) { hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0); - ExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken); + CabcExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken); } ++pcd->cDuplicates; @@ -839,10 +855,10 @@ static HRESULT AddNonDuplicateFile( size_t cbFilePaths = 0; hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths); - ExitOnFailure(hr, "Maximum allocation exceeded."); + CabcExitOnFailure(hr, "Maximum allocation exceeded."); pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file."); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file."); ZeroMemory(reinterpret_cast(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE)); @@ -859,7 +875,7 @@ static HRESULT AddNonDuplicateFile( if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize) { pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - ExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash"); + CabcExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash"); pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); pcf->pmfHash->dwData[0] = pmfHash->dwData[0]; @@ -869,18 +885,18 @@ static HRESULT AddNonDuplicateFile( } hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0); - ExitOnFailure(hr, "Failed to copy file path: %ls", wzFile); + CabcExitOnFailure(hr, "Failed to copy file path: %ls", wzFile); if (wzToken && *wzToken) { hr = StrAllocString(&pcf->pwzToken, wzToken, 0); - ExitOnFailure(hr, "Failed to copy file token: %ls", wzToken); + CabcExitOnFailure(hr, "Failed to copy file token: %ls", wzToken); } ++pcd->cFilePaths; hr = DictAddValue(pcd->shDictHandle, pcf); - ExitOnFailure(hr, "Failed to add file to dictionary of added files"); + CabcExitOnFailure(hr, "Failed to add file to dictionary of added files"); LExit: ReleaseMem(pv); @@ -903,14 +919,14 @@ static HRESULT UpdateDuplicateFiles( hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hCabinet) { - ExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); + CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); } // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as // the upper bound for the memory map. if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) { - ExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); + CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); } if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) @@ -926,11 +942,11 @@ static HRESULT UpdateDuplicateFiles( hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) { - ExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); + CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); } pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); - ExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); + CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); pCabinetHeader = static_cast(pv); @@ -939,7 +955,7 @@ static HRESULT UpdateDuplicateFiles( const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i; hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile); - ExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex); + CabcExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex); } LExit: @@ -974,7 +990,7 @@ static HRESULT DuplicateFile( pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex); + CabcExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex); } // Step through each cabinet items until we get to the original @@ -1002,7 +1018,7 @@ static HRESULT DuplicateFile( if (0 != pDuplicateItem->cbFile) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile); + CabcExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile); } pDuplicateItem->cbFile = pOriginalItem->cbFile; @@ -1031,12 +1047,12 @@ static HRESULT UtcFileTimeToLocalDosDateTime( if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal)) { - ExitWithLastError(hr, "Filed to convert file time to local file time."); + CabcExitWithLastError(hr, "Filed to convert file time to local file time."); } if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime)) { - ExitWithLastError(hr, "Filed to convert file time to DOS date time."); + CabcExitWithLastError(hr, "Filed to convert file time to DOS date time."); } LExit: @@ -1053,7 +1069,7 @@ static __callback int DIAMONDAPI CabCFilePlaced( __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { UNREFERENCED_PARAMETER(pccab); @@ -1085,7 +1101,7 @@ static __callback INT_PTR DIAMONDAPI CabCOpen( __in int oflag, __in int pmode, __out int *err, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { CABC_DATA *pcd = reinterpret_cast(pv); @@ -1139,7 +1155,7 @@ static __callback INT_PTR DIAMONDAPI CabCOpen( if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) { - ExitOnLastError(hr, "failed to open file: %s", pszFile); + CabcExitOnLastError(hr, "failed to open file: %s", pszFile); } LExit: @@ -1155,18 +1171,18 @@ static __callback UINT FAR DIAMONDAPI CabCRead( __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { CABC_DATA *pcd = reinterpret_cast(pv); HRESULT hr = S_OK; DWORD cbRead = 0; - ExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided"); + CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided"); if (!::ReadFile(reinterpret_cast(hf), memory, cb, &cbRead, NULL)) { *err = ::GetLastError(); - ExitOnLastError(hr, "failed to read during cabinet extraction"); + CabcExitOnLastError(hr, "failed to read during cabinet extraction"); } LExit: @@ -1184,18 +1200,18 @@ static __callback UINT FAR DIAMONDAPI CabCWrite( __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { CABC_DATA *pcd = reinterpret_cast(pv); HRESULT hr = S_OK; DWORD cbWrite = 0; - ExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided"); + CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided"); if (!::WriteFile(reinterpret_cast(hf), memory, cb, &cbWrite, NULL)) { *err = ::GetLastError(); - ExitOnLastError(hr, "failed to write during cabinet extraction"); + CabcExitOnLastError(hr, "failed to write during cabinet extraction"); } LExit: @@ -1211,7 +1227,7 @@ static __callback long FAR DIAMONDAPI CabCSeek( __in long dist, __in int seektype, __out int *err, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { CABC_DATA *pcd = reinterpret_cast(pv); @@ -1233,7 +1249,7 @@ static __callback long FAR DIAMONDAPI CabCSeek( default : dwMoveMethod = 0; hr = E_UNEXPECTED; - ExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype); + CabcExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype); } // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. @@ -1243,7 +1259,7 @@ static __callback long FAR DIAMONDAPI CabCSeek( if (DWORD_MAX == lMove) { *err = ::GetLastError(); - ExitOnLastError(hr, "failed to move file pointer %d bytes", dist); + CabcExitOnLastError(hr, "failed to move file pointer %d bytes", dist); } LExit: @@ -1259,7 +1275,7 @@ LExit: static __callback int FAR DIAMONDAPI CabCClose( __in INT_PTR hf, __out int *err, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { CABC_DATA *pcd = reinterpret_cast(pv); @@ -1268,7 +1284,7 @@ static __callback int FAR DIAMONDAPI CabCClose( if (!::CloseHandle(reinterpret_cast(hf))) { *err = ::GetLastError(); - ExitOnLastError(hr, "failed to close file during cabinet extraction"); + CabcExitOnLastError(hr, "failed to close file during cabinet extraction"); } LExit: @@ -1283,7 +1299,7 @@ LExit: static __callback int DIAMONDAPI CabCDelete( __in_z PSTR szFile, __out int *err, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { UNREFERENCED_PARAMETER(err); @@ -1302,7 +1318,7 @@ __success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile( __out_bcount_z(cbFile) char *szFile, __in int cbFile, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { CABC_DATA *pcd = reinterpret_cast(pv); @@ -1316,7 +1332,7 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile( if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) { - ExitWithLastError(hr, "Failed to get temp path during cabinet creation."); + CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation."); } for (DWORD i = 0; i < DWORD_MAX; ++i) @@ -1324,7 +1340,7 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile( LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast(&dwIndex)); hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); - ExitOnFailure(hr, "failed to format log file path."); + CabcExitOnFailure(hr, "failed to format log file path."); hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (INVALID_HANDLE_VALUE != hTempFile) @@ -1338,7 +1354,7 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile( hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. } } - ExitOnFailure(hr, "failed to find temporary file."); + CabcExitOnFailure(hr, "failed to find temporary file."); LExit: ReleaseFileHandle(hTempFile); @@ -1377,7 +1393,7 @@ static __callback BOOL DIAMONDAPI CabCGetNextCabinet( len -= 4; // remove Extention ".cab" of 8.3 Format } hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len); - ExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name"); + CabcExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name"); } const int nAlphabets = 26; // Number of Alphabets from a to z @@ -1385,9 +1401,9 @@ static __callback BOOL DIAMONDAPI CabCGetNextCabinet( { // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab)); - ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab)); - ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); } else if (pccab->iCab <= nAlphabets*nAlphabets) { @@ -1401,14 +1417,14 @@ static __callback BOOL DIAMONDAPI CabCGetNextCabinet( char1--; // First Char must be decremented by 1 } hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2)); - ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2)); - ExitOnFailure(hr, "Failed to create next Cabinet File Name"); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); } else { hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index. - ExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name"); + CabcExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name"); } // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method @@ -1478,7 +1494,7 @@ static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo( if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad)) { - ExitWithLastError(hr, "Failed to get file attributes on '%s'.", pFileInfo->wzSourcePath); + CabcExitWithLastError(hr, "Failed to get file attributes on '%ls'.", pFileInfo->wzSourcePath); } // Set the attributes but only allow the few attributes that CAB supports. @@ -1492,7 +1508,7 @@ static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo( // found. This would create further problems if the file was written to the CAB without this value. Windows // Installer would then fail to extract the file. hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime); - ExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName); + CabcExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName); } iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv); @@ -1512,7 +1528,7 @@ static __callback long DIAMONDAPI CabCStatus( __in UINT ui, __in ULONG cb1, __in ULONG cb2, - __out_bcount(CABC_HANDLE_BYTES) void *pv + __inout_bcount(CABC_HANDLE_BYTES) void *pv ) { UNREFERENCED_PARAMETER(ui); diff --git a/src/dutil/cabutil.cpp b/src/dutil/cabutil.cpp index e0efb717..4a6f7b7b 100644 --- a/src/dutil/cabutil.cpp +++ b/src/dutil/cabutil.cpp @@ -2,6 +2,22 @@ #include "precomp.h" + +// Exit macros +#define CabExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) +#define CabExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) +#define CabExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) +#define CabExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) +#define CabExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABUTIL, e, x, s, __VA_ARGS__) +#define CabExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABUTIL, g, x, s, __VA_ARGS__) + + // external prototypes typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*); typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); @@ -59,20 +75,20 @@ inline HRESULT LoadCabinetDll() if (!vhCabinetDll) { hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll); - ExitOnFailure(hr, "failed to load cabinet.dll"); + CabExitOnFailure(hr, "failed to load cabinet.dll"); // retrieve all address functions vpfnFDICreate = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICreate")); - ExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL"); + CabExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL"); vpfnFDICopy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICopy")); - ExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL"); + CabExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL"); vpfnFDIIsCabinet = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIIsCabinet")); - ExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL"); + CabExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL"); vpfnFDIDestroy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIDestroy")); - ExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL"); + CabExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL"); vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf); - ExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll"); + CabExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll"); } LExit: @@ -99,7 +115,7 @@ extern "C" HRESULT DAPI CabInitialize( if (!fDelayLoad) { hr = LoadCabinetDll(); - ExitOnFailure(hr, "failed to load CABINET.DLL"); + CabExitOnFailure(hr, "failed to load CABINET.DLL"); } LExit: @@ -143,8 +159,8 @@ extern "C" void DAPI CabUninitialize( in the cabinet ********************************************************************/ extern "C" HRESULT DAPI CabEnumerate( - __in LPCWSTR wzCabinet, - __in LPCWSTR wzEnumerateFile, + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzEnumerateFile, __in STDCALL_PFNFDINOTIFY pfnNotify, __in DWORD64 dw64EmbeddedOffset ) @@ -161,9 +177,9 @@ extern "C" HRESULT DAPI CabEnumerate( if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa ********************************************************************/ extern "C" HRESULT DAPI CabExtract( - __in LPCWSTR wzCabinet, - __in LPCWSTR wzExtractFile, - __in LPCWSTR wzExtractDir, + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzExtractFile, + __in_z LPCWSTR wzExtractDir, __in_opt CAB_CALLBACK_PROGRESS pfnProgress, __in_opt LPVOID pvContext, __in DWORD64 dw64EmbeddedOffset @@ -238,21 +254,21 @@ static HRESULT DAPI CabOperation( if (!vhfdi) { hr = LoadCabinetDll(); - ExitOnFailure(hr, "failed to load CABINET.DLL"); + CabExitOnFailure(hr, "failed to load CABINET.DLL"); } hr = StrAllocString(&sczCabinet, wzCabinet, 0); - ExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet); + CabExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet); // // split the cabinet full path into directory and filename and convert to multi-byte (ick!) // pwz = FileFromPath(sczCabinet); - ExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet); + CabExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet); if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL)) { - ExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz); + CabExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz); } *pwz = '\0'; @@ -261,13 +277,13 @@ static HRESULT DAPI CabOperation( if (wzCabinet == pwz) { hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); - ExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); + CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); } else { if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) { - ExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); + CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); } } @@ -295,7 +311,7 @@ static HRESULT DAPI CabOperation( fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure { - ExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); + CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); } LExit: @@ -331,22 +347,22 @@ static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __i if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); + CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); } hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); - ExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); + CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); pFile = reinterpret_cast(::CreateFileW(sczCabFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) { - ExitWithLastError(hr, "failed to open file: %ls", sczCabFile); + CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile); } if (vdw64EmbeddedOffset) { hr = CabExtractSeek(pFile, 0, 0); - ExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); + CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); } LExit: @@ -361,10 +377,10 @@ static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void HRESULT hr = S_OK; DWORD cbRead = 0; - ExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read"); + CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read"); if (!::ReadFile(reinterpret_cast(hf), pv, cb, &cbRead, NULL)) { - ExitWithLastError(hr, "failed to read during cabinet extraction"); + CabExitWithLastError(hr, "failed to read during cabinet extraction"); } LExit: @@ -377,10 +393,10 @@ static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void HRESULT hr = S_OK; DWORD cbWrite = 0; - ExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write"); + CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write"); if (!::WriteFile(reinterpret_cast(hf), pv, cb, &cbWrite, NULL)) { - ExitWithLastError(hr, "failed to write during cabinet extraction"); + CabExitWithLastError(hr, "failed to write during cabinet extraction"); } LExit: @@ -409,7 +425,7 @@ static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long default : dwMoveMethod = 0; hr = E_UNEXPECTED; - ExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); } // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. @@ -417,7 +433,7 @@ static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); if (0xFFFFFFFF == lMove) { - ExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist); } LExit: @@ -431,7 +447,7 @@ static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf) if (!::CloseHandle(reinterpret_cast(hf))) { - ExitWithLastError(hr, "failed to close file during cabinet extraction"); + CabExitWithLastError(hr, "failed to close file during cabinet extraction"); } LExit: @@ -454,8 +470,8 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE switch (iNotification) { case fdintCOPY_FILE: // begin extracting a resource from cabinet - ExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); - ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); if (pccs->fStopExtracting) { @@ -466,7 +482,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE sz = static_cast(pFDINotify->psz1); if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) { - ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); } if (pccs->pfnProgress) @@ -484,21 +500,21 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE FILETIME ftLocal; if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) { - ExitWithLastError(hr, "failed to get time for resource: %ls", wz); + CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); } ::LocalFileTimeToFileTime(&ftLocal, &ft); WCHAR wzPath[MAX_PATH]; hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); - ExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); + CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); ipResult = reinterpret_cast(::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); if (INVALID_HANDLE_VALUE == reinterpret_cast(ipResult)) { - ExitWithLastError(hr, "failed to create file: %s", wzPath); + CabExitWithLastError(hr, "failed to create file: %ls", wzPath); } ::SetFileTime(reinterpret_cast(ipResult), &ft, &ft, &ft); // try to set the file time (who cares if it fails) @@ -520,15 +536,15 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE break; case fdintCLOSE_FILE_INFO: // resource extraction complete Assert(pFDINotify->hf && pFDINotify->psz1); - ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); // convert params to useful variables sz = static_cast(pFDINotify->psz1); - ExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); + CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) { - ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); } if (NULL != pFDINotify->hf) // just close the file diff --git a/src/dutil/certutil.cpp b/src/dutil/certutil.cpp index 9c0ee256..69897b9e 100644 --- a/src/dutil/certutil.cpp +++ b/src/dutil/certutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define CertExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) +#define CertExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) +#define CertExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) +#define CertExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) +#define CertExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CERTUTIL, e, x, s, __VA_ARGS__) +#define CertExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CERTUTIL, g, x, s, __VA_ARGS__) + /******************************************************************** CertReadProperty - reads a property from the certificate. @@ -20,15 +35,15 @@ extern "C" HRESULT DAPI CertReadProperty( if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb)) { - ExitWithLastError(hr, "Failed to get size of certificate property."); + CertExitWithLastError(hr, "Failed to get size of certificate property."); } pv = MemAlloc(cb, TRUE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property."); + CertExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property."); if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb)) { - ExitWithLastError(hr, "Failed to get certificate property."); + CertExitWithLastError(hr, "Failed to get certificate property."); } *ppvValue = pv; @@ -70,11 +85,11 @@ extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp( if (!pBlob) { hr = TRUST_E_FAIL; - ExitOnFailure(hr, "Failed to find countersigner in signer information."); + CertExitOnFailure(hr, "Failed to find countersigner in signer information."); } hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast(&pCounterSignerInfo), NULL); - ExitOnFailure(hr, "Failed to decode countersigner information."); + CertExitOnFailure(hr, "Failed to decode countersigner information."); pBlob = NULL; // reset the blob before searching for the signing time. @@ -91,12 +106,12 @@ extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp( if (!pBlob) { hr = TRUST_E_FAIL; - ExitOnFailure(hr, "Failed to find signing time in countersigner information."); + CertExitOnFailure(hr, "Failed to find signing time in countersigner information."); } if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp)) { - ExitWithLastError(hr, "Failed to decode countersigner signing timestamp."); + CertExitWithLastError(hr, "Failed to decode countersigner signing timestamp."); } LExit: @@ -124,10 +139,10 @@ extern "C" HRESULT DAPI GetCryptProvFromCert( GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL; hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); - ExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert"); - ExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); if (!pGetCryptProvFromCert(hwnd, pCert, @@ -138,7 +153,7 @@ extern "C" HRESULT DAPI GetCryptProvFromCert( ppwszProviderName, pdwProviderType)) { - ExitWithLastError(hr, "Failed to get CSP from cert."); + CertExitWithLastError(hr, "Failed to get CSP from cert."); } LExit: return hr; @@ -159,10 +174,10 @@ extern "C" HRESULT DAPI FreeCryptProvFromCert( FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL; hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); - ExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert"); - ExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer); LExit: @@ -185,12 +200,12 @@ extern "C" HRESULT DAPI GetProvSecurityDesc( &ulSize, DACL_SECURITY_INFORMATION)) { - ExitWithLastError(hr, "Error getting security descriptor size for CSP."); + CertExitWithLastError(hr, "Error getting security descriptor size for CSP."); } // Allocate the memory for the security descriptor. pSecurity = static_cast(MemAlloc(ulSize, TRUE)); - ExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL"); + CertExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL"); // Get the security descriptor. if (!::CryptGetProvParam( @@ -201,7 +216,7 @@ extern "C" HRESULT DAPI GetProvSecurityDesc( DACL_SECURITY_INFORMATION)) { MemFree(pSecurity); - ExitWithLastError(hr, "Error getting security descriptor for CSP."); + CertExitWithLastError(hr, "Error getting security descriptor for CSP."); } *ppSecurity = pSecurity; @@ -223,7 +238,7 @@ extern "C" HRESULT DAPI SetProvSecurityDesc( (BYTE*)pSecurity, DACL_SECURITY_INFORMATION)) { - ExitWithLastError(hr, "Error setting security descriptor for CSP."); + CertExitWithLastError(hr, "Error setting security descriptor for CSP."); } LExit: return hr; @@ -278,12 +293,12 @@ extern "C" HRESULT DAPI CertInstallSingleCertificate( if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob)) { - ExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName); + CertExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName); } if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) { - ExitWithLastError(hr, "Failed to add certificate to the store."); + CertExitWithLastError(hr, "Failed to add certificate to the store."); } // if the certificate has a private key, grant Administrators access @@ -293,16 +308,16 @@ extern "C" HRESULT DAPI CertInstallSingleCertificate( { // We added a CSP key hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType); - ExitOnFailure(hr, "Failed to get handle to CSP"); + CertExitOnFailure(hr, "Failed to get handle to CSP"); hr = GetProvSecurityDesc(hCsp, &pSecurity); - ExitOnFailure(hr, "Failed to get security descriptor of CSP"); + CertExitOnFailure(hr, "Failed to get security descriptor of CSP"); hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew); - ExitOnFailure(hr, "Failed to create new security descriptor"); + CertExitOnFailure(hr, "Failed to create new security descriptor"); hr = SetProvSecurityDesc(hCsp, pSecurityNew); - ExitOnFailure(hr, "Failed to set Admin ACL on CSP"); + CertExitOnFailure(hr, "Failed to set Admin ACL on CSP"); } if (CERT_NCRYPT_KEY_SPEC == dwKeySpec) diff --git a/src/dutil/conutil.cpp b/src/dutil/conutil.cpp index 4c820a1c..33e1b59a 100644 --- a/src/dutil/conutil.cpp +++ b/src/dutil/conutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__) +#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__) + + static HANDLE vhStdIn = INVALID_HANDLE_VALUE; static HANDLE vhStdOut = INVALID_HANDLE_VALUE; static BOOL vfConsoleIn = FALSE; @@ -19,13 +34,13 @@ extern "C" HRESULT DAPI ConsoleInitialize() vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); if (INVALID_HANDLE_VALUE == vhStdIn) { - ExitOnLastError(hr, "failed to open stdin"); + ConExitOnLastError(hr, "failed to open stdin"); } vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); if (INVALID_HANDLE_VALUE == vhStdOut) { - ExitOnLastError(hr, "failed to open stdout"); + ConExitOnLastError(hr, "failed to open stdout"); } // check if we have a std in on the console @@ -43,7 +58,7 @@ extern "C" HRESULT DAPI ConsoleInitialize() } else { - ExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); + ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); } } @@ -62,7 +77,7 @@ extern "C" HRESULT DAPI ConsoleInitialize() } else { - ExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); + ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); } } @@ -89,6 +104,8 @@ LExit: extern "C" void DAPI ConsoleUninitialize() { + BOOL fOutEqualsIn = vhStdOut == vhStdIn; + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); if (INVALID_HANDLE_VALUE != vhStdOut) @@ -96,7 +113,7 @@ extern "C" void DAPI ConsoleUninitialize() ::CloseHandle(vhStdOut); } - if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) + if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn) { ::CloseHandle(vhStdIn); } @@ -178,14 +195,14 @@ extern "C" HRESULT DAPI ConsoleWrite( va_start(args, szFormat); hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); va_end(args); - ExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); cchOutput = lstrlenA(pszOutput); while (cbTotal < (sizeof(*pszOutput) * cchOutput)) { if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) { - ExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); } cbTotal += cbWrote; @@ -236,7 +253,7 @@ extern "C" HRESULT DAPI ConsoleWriteLine( va_start(args, szFormat); hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); va_end(args); - ExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); // // write the string @@ -245,7 +262,7 @@ extern "C" HRESULT DAPI ConsoleWriteLine( while (cbTotal < (sizeof(*pszOutput) * cchOutput)) { if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) - ExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); cbTotal += cbWrote; } @@ -255,7 +272,7 @@ extern "C" HRESULT DAPI ConsoleWriteLine( // if (!::WriteFile(vhStdOut, reinterpret_cast(szNewLine), 2, &cbWrote, NULL)) { - ExitOnLastError(hr, "failed to write newline to console"); + ConExitOnLastError(hr, "failed to write newline to console"); } // reset the color to normal @@ -289,7 +306,7 @@ HRESULT ConsoleWriteError( va_start(args, szFormat); hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args); va_end(args); - ExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); + ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); if (FAILED(hrError)) { @@ -326,14 +343,14 @@ extern "C" HRESULT DAPI ConsoleReadW( cch = 64; hr = StrAnsiAlloc(&psz, cch); - ExitOnFailure(hr, "failed to allocate memory to read from console"); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); // loop until we read the \r\n from the console for (;;) { // read one character at a time, since that seems to be the only way to make this work if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) - ExitOnLastError(hr, "failed to read string from console"); + ConExitOnLastError(hr, "failed to read string from console"); cchTotalRead += cchRead; if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1]) @@ -351,7 +368,7 @@ extern "C" HRESULT DAPI ConsoleReadW( { cch *= 2; // double everytime we run out of space hr = StrAnsiAlloc(&psz, cch); - ExitOnFailure(hr, "failed to allocate memory to read from console"); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); } } @@ -381,7 +398,7 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW( LPSTR psz = NULL; - ExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); + ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); DWORD dwRead; DWORD dwNumInput; @@ -412,7 +429,7 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW( if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead)) { - ExitOnLastError(hr, "failed to peek at console input"); + ConExitOnLastError(hr, "failed to peek at console input"); } if (0 == dwRead) @@ -424,7 +441,7 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW( { if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput)) { - ExitOnLastError(hr, "Failed to read input from console"); + ConExitOnLastError(hr, "Failed to read input from console"); } // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested @@ -463,14 +480,14 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW( cch = 8; hr = StrAnsiAlloc(&psz, cch); - ExitOnFailure(hr, "failed to allocate memory to read from console"); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--) { // read one character at a time, since that seems to be the only way to make this work if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) { - ExitOnLastError(hr, "failed to read string from console"); + ConExitOnLastError(hr, "failed to read string from console"); } cchTotalRead += cchRead; @@ -490,7 +507,7 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW( { cch *= 2; // double everytime we run out of space hr = StrAnsiAlloc(&psz, cch); - ExitOnFailure(hr, "failed to allocate memory to read from console"); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); } } @@ -510,7 +527,7 @@ LExit: *********************************************************************/ extern "C" HRESULT DAPI ConsoleReadStringA( - __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, CONST DWORD cchCharBuffer, __out DWORD* pcchNumCharReturn ) @@ -526,11 +543,11 @@ extern "C" HRESULT DAPI ConsoleReadStringA( do { hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); - ExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); // ReadConsoleW will not return until , the last two chars are 13 and 10. if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) { - ExitOnLastError(hr, "failed to read string from console"); + ConExitOnLastError(hr, "failed to read string from console"); } iReadCharTotal += *pcchNumCharReturn; iRead += 1; @@ -543,7 +560,7 @@ extern "C" HRESULT DAPI ConsoleReadStringA( if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) { - ExitOnLastError(hr, "failed to read string from console"); + ConExitOnLastError(hr, "failed to read string from console"); } if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) @@ -567,7 +584,7 @@ LExit: *********************************************************************/ extern "C" HRESULT DAPI ConsoleReadStringW( - __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, const DWORD cchCharBuffer, __out DWORD* pcchNumCharReturn ) @@ -583,11 +600,11 @@ extern "C" HRESULT DAPI ConsoleReadStringW( do { hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); - ExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); // ReadConsoleW will not return until , the last two chars are 13 and 10. if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) { - ExitOnLastError(hr, "failed to read string from console"); + ConExitOnLastError(hr, "failed to read string from console"); } iReadCharTotal += *pcchNumCharReturn; iRead += 1; @@ -600,7 +617,7 @@ extern "C" HRESULT DAPI ConsoleReadStringW( if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) { - ExitOnLastError(hr, "failed to read string from console"); + ConExitOnLastError(hr, "failed to read string from console"); } if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) @@ -630,7 +647,7 @@ extern "C" HRESULT DAPI ConsoleSetReadHidden(void) ::FlushConsoleInputBuffer(vhStdIn); if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) { - ExitOnLastError(hr, "failed to set console input mode to be hidden"); + ConExitOnLastError(hr, "failed to set console input mode to be hidden"); } LExit: @@ -647,7 +664,7 @@ extern "C" HRESULT DAPI ConsoleSetReadNormal(void) HRESULT hr = S_OK; if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) { - ExitOnLastError(hr, "failed to set console input mode to be normal"); + ConExitOnLastError(hr, "failed to set console input mode to be normal"); } LExit: diff --git a/src/dutil/cryputil.cpp b/src/dutil/cryputil.cpp index 214704b4..c5c1b221 100644 --- a/src/dutil/cryputil.cpp +++ b/src/dutil/cryputil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define CrypExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) +#define CrypExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) +#define CrypExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) +#define CrypExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) +#define CrypExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CRYPUTIL, e, x, s, __VA_ARGS__) +#define CrypExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CRYPUTIL, g, x, s, __VA_ARGS__) + static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL; static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL; static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL; @@ -32,17 +47,17 @@ extern "C" HRESULT DAPI CrypInitialize( if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory) { hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll); - ExitOnFailure(hr, "Failed to load Crypt32.dll"); + CrypExitOnFailure(hr, "Failed to load Crypt32.dll"); vpfnCryptProtectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory")); if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory) { - ExitWithLastError(hr, "Failed to load an encryption method"); + CrypExitWithLastError(hr, "Failed to load an encryption method"); } vpfnCryptUnprotectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory")); if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory) { - ExitWithLastError(hr, "Failed to load a decryption method"); + CrypExitWithLastError(hr, "Failed to load a decryption method"); } } @@ -94,15 +109,15 @@ extern "C" HRESULT DAPI CrypDecodeObject( if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject)) { - ExitWithLastError(hr, "Failed to decode object to determine size."); + CrypExitWithLastError(hr, "Failed to decode object to determine size."); } pvObject = MemAlloc(cbObject, TRUE); - ExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object."); + CrypExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object."); if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject)) { - ExitWithLastError(hr, "Failed to decode object."); + CrypExitWithLastError(hr, "Failed to decode object."); } *ppvObject = pvObject; @@ -134,15 +149,15 @@ extern "C" HRESULT DAPI CrypMsgGetParam( if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb)) { - ExitWithLastError(hr, "Failed to get crypt message parameter data size."); + CrypExitWithLastError(hr, "Failed to get crypt message parameter data size."); } pv = MemAlloc(cb, TRUE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter."); + CrypExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter."); if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb)) { - ExitWithLastError(hr, "Failed to get crypt message parameter."); + CrypExitWithLastError(hr, "Failed to get crypt message parameter."); } *ppvData = pv; @@ -161,7 +176,7 @@ LExit: extern "C" HRESULT DAPI CrypHashFile( - __in LPCWSTR wzFilePath, + __in_z LPCWSTR wzFilePath, __in DWORD dwProvType, __in ALG_ID algid, __out_bcount(cbHash) BYTE* pbHash, @@ -176,11 +191,11 @@ extern "C" HRESULT DAPI CrypHashFile( hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) { - ExitWithLastError(hr, "Failed to open input file."); + CrypExitWithLastError(hr, "Failed to open input file."); } hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed); - ExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath); + CrypExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath); LExit: ReleaseFileHandle(hFile); @@ -208,13 +223,13 @@ extern "C" HRESULT DAPI CrypHashFileHandle( // get handle to the crypto provider if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - ExitWithLastError(hr, "Failed to acquire crypto context."); + CrypExitWithLastError(hr, "Failed to acquire crypto context."); } // initiate hash if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) { - ExitWithLastError(hr, "Failed to initiate hash."); + CrypExitWithLastError(hr, "Failed to initiate hash."); } for (;;) @@ -222,7 +237,7 @@ extern "C" HRESULT DAPI CrypHashFileHandle( // read data block if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL)) { - ExitWithLastError(hr, "Failed to read data block."); + CrypExitWithLastError(hr, "Failed to read data block."); } if (!cbRead) @@ -233,21 +248,21 @@ extern "C" HRESULT DAPI CrypHashFileHandle( // hash data block if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0)) { - ExitWithLastError(hr, "Failed to hash data block."); + CrypExitWithLastError(hr, "Failed to hash data block."); } } // get hash value if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) { - ExitWithLastError(hr, "Failed to get hash value."); + CrypExitWithLastError(hr, "Failed to get hash value."); } if (pqwBytesHashed) { if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT)) { - ExitWithLastError(hr, "Failed to get file pointer."); + CrypExitWithLastError(hr, "Failed to get file pointer."); } } @@ -280,24 +295,24 @@ HRESULT DAPI CrypHashBuffer( // get handle to the crypto provider if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - ExitWithLastError(hr, "Failed to acquire crypto context."); + CrypExitWithLastError(hr, "Failed to acquire crypto context."); } // initiate hash if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) { - ExitWithLastError(hr, "Failed to initiate hash."); + CrypExitWithLastError(hr, "Failed to initiate hash."); } if (!::CryptHashData(hHash, pbBuffer, static_cast(cbBuffer), 0)) { - ExitWithLastError(hr, "Failed to hash data."); + CrypExitWithLastError(hr, "Failed to hash data."); } // get hash value if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) { - ExitWithLastError(hr, "Failed to get hash value."); + CrypExitWithLastError(hr, "Failed to get hash value."); } LExit: @@ -340,7 +355,7 @@ HRESULT DAPI CrypEncryptMemory( hr = HRESULT_FROM_WIN32(::GetLastError()); } } - ExitOnFailure(hr, "Failed to encrypt memory"); + CrypExitOnFailure(hr, "Failed to encrypt memory"); LExit: return hr; } @@ -372,7 +387,7 @@ HRESULT DAPI CrypDecryptMemory( hr = HRESULT_FROM_WIN32(::GetLastError()); } } - ExitOnFailure(hr, "Failed to decrypt memory"); + CrypExitOnFailure(hr, "Failed to decrypt memory"); LExit: return hr; } diff --git a/src/dutil/deputil.cpp b/src/dutil/deputil.cpp index d65c4348..362b3345 100644 --- a/src/dutil/deputil.cpp +++ b/src/dutil/deputil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define DepExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) +#define DepExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) +#define DepExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) +#define DepExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) +#define DepExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEPUTIL, e, x, s, __VA_ARGS__) +#define DepExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEPUTIL, g, x, s, __VA_ARGS__) + #define ARRAY_GROWTH_SIZE 5 static LPCWSTR vcszVersionValue = L"Version"; @@ -42,7 +57,7 @@ DAPI_(HRESULT) DepGetProviderInformation( // Format the provider dependency registry key. hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); // Try to open the dependency key. hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); @@ -50,7 +65,7 @@ DAPI_(HRESULT) DepGetProviderInformation( { ExitFunction1(hr = E_NOTFOUND); } - ExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); // Get the Id if requested and available. if (psczId) @@ -60,7 +75,7 @@ DAPI_(HRESULT) DepGetProviderInformation( { hr = S_OK; } - ExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey); } // Get the DisplayName if requested and available. @@ -71,7 +86,7 @@ DAPI_(HRESULT) DepGetProviderInformation( { hr = S_OK; } - ExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey); } // Get the Version if requested and available. @@ -82,7 +97,7 @@ DAPI_(HRESULT) DepGetProviderInformation( { hr = S_OK; } - ExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey); } LExit: @@ -116,19 +131,19 @@ DAPI_(HRESULT) DepCheckDependency( // Format the provider dependency registry key. hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); // Try to open the key. If that fails, add the missing dependency key to the dependency array if it doesn't already exist. hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); // If there are no registry values, consider the key orphaned and treat it as missing. hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); + DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); } } @@ -138,15 +153,15 @@ DAPI_(HRESULT) DepCheckDependency( hr = DictKeyExists(sdDependencies, wzProviderKey); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey); } else { hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, NULL); - ExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey); + DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey); hr = DictAddKey(sdDependencies, wzProviderKey); - ExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey); + DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey); } // Exit since the check already failed. @@ -160,7 +175,7 @@ DAPI_(HRESULT) DepCheckDependency( if (0 < cchMinVersion) { hr = FileVersionFromStringEx(wzMinVersion, cchMinVersion, &dw64MinVersion); - ExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); + DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) @@ -168,18 +183,18 @@ DAPI_(HRESULT) DepCheckDependency( hr = DictKeyExists(sdDependencies, wzProviderKey); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey); } else { hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); - ExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey); hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); - ExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey); + DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey); hr = DictAddKey(sdDependencies, wzProviderKey); - ExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey); } // Exit since the check already failed. @@ -195,7 +210,7 @@ DAPI_(HRESULT) DepCheckDependency( if (0 < cchMaxVersion) { hr = FileVersionFromStringEx(wzMaxVersion, cchMaxVersion, &dw64MaxVersion); - ExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); + DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) @@ -203,18 +218,18 @@ DAPI_(HRESULT) DepCheckDependency( hr = DictKeyExists(sdDependencies, wzProviderKey); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey); } else { hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); - ExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey); hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); - ExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey); + DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey); hr = DictAddKey(sdDependencies, wzProviderKey); - ExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey); } // Exit since the check already failed. @@ -249,17 +264,17 @@ DAPI_(HRESULT) DepCheckDependents( // Format the provider dependency registry key. hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); // Try to open the key. If that fails, the dependency information is corrupt. hr = RegOpen(hkHive, sczKey, KEY_READ, &hkProviderKey); - ExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey); + DepExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey); // Try to open the dependencies key. If that does not exist, there are no dependents. hr = RegOpen(hkProviderKey, vsczRegistryDependents, KEY_READ, &hkDependentsKey); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey); } else { @@ -272,7 +287,7 @@ DAPI_(HRESULT) DepCheckDependents( hr = RegKeyEnum(hkDependentsKey, dwIndex, &sczDependentKey); if (E_NOMOREITEMS != hr) { - ExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey); } else { @@ -284,16 +299,16 @@ DAPI_(HRESULT) DepCheckDependents( hr = DictKeyExists(sdIgnoredDependents, sczDependentKey); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + DepExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); } else { // Get the name of the dependent from the key. hr = GetDependencyNameFromKey(hkHive, sczDependentKey, &sczDependentName); - ExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey); + DepExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey); hr = DepDependencyArrayAlloc(prgDependents, pcDependents, sczDependentKey, sczDependentName); - ExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey); + DepExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey); } } @@ -323,32 +338,32 @@ DAPI_(HRESULT) DepRegisterDependency( // Format the provider dependency registry key. hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); // Create the dependency key (or open it if it already exists). hr = RegCreateEx(hkHive, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); - ExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey); + DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey); // Set the id if it was provided. if (wzId) { hr = RegWriteString(hkKey, NULL, wzId); - ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId); } // Set the version. hr = RegWriteString(hkKey, vcszVersionValue, wzVersion); - ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion); // Set the display name. hr = RegWriteString(hkKey, vcszDisplayNameValue, wzDisplayName); - ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName); // Set the attributes if non-zero. if (0 != iAttributes) { hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); - ExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); } LExit: @@ -370,12 +385,12 @@ DAPI_(HRESULT) DepDependentExists( // Format the provider dependents registry key. hr = StrAllocFormatted(&sczDependentKey, L"%ls%ls\\%ls\\%ls", vsczRegistryRoot, wzDependencyProviderKey, vsczRegistryDependents, wzProviderKey); - ExitOnFailure(hr, "Failed to format registry key to dependent."); + DepExitOnFailure(hr, "Failed to format registry key to dependent."); hr = RegOpen(hkHive, sczDependentKey, KEY_READ, &hkDependentKey); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey); + DepExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey); } LExit: @@ -403,32 +418,32 @@ DAPI_(HRESULT) DepRegisterDependent( // Format the provider dependency registry key. hr = AllocDependencyKeyName(wzDependencyProviderKey, &sczDependencyKey); - ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey); // Create the dependency key (or open it if it already exists). hr = RegCreateEx(hkHive, sczDependencyKey, KEY_WRITE, FALSE, NULL, &hkDependencyKey, &fCreated); - ExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey); + DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey); // Create the subkey to register the dependent. hr = StrAllocFormatted(&sczKey, L"%ls\\%ls", vsczRegistryDependents, wzProviderKey); - ExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); hr = RegCreateEx(hkDependencyKey, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); - ExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey); + DepExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey); // Set the minimum version if not NULL. hr = RegWriteString(hkKey, vcszMinVersionValue, wzMinVersion); - ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion); // Set the maximum version if not NULL. hr = RegWriteString(hkKey, vcszMaxVersionValue, wzMaxVersion); - ExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion); // Set the attributes if non-zero. if (0 != iAttributes) { hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); - ExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); } LExit: @@ -451,13 +466,13 @@ DAPI_(HRESULT) DepUnregisterDependency( // Format the provider dependency registry key. hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); // Delete the entire key including all sub-keys. hr = RegDelete(hkHive, sczKey, REG_KEY_DEFAULT, TRUE); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey); + DepExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey); } LExit: @@ -484,7 +499,7 @@ DAPI_(HRESULT) DepUnregisterDependent( hr = RegOpen(hkHive, vsczRegistryRoot, KEY_READ, &hkRegistryRoot); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot); + DepExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot); } else { @@ -495,7 +510,7 @@ DAPI_(HRESULT) DepUnregisterDependent( hr = RegOpen(hkRegistryRoot, wzDependencyProviderKey, KEY_READ, &hkDependencyProviderKey); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey); } else { @@ -506,7 +521,7 @@ DAPI_(HRESULT) DepUnregisterDependent( hr = RegOpen(hkDependencyProviderKey, vsczRegistryDependents, KEY_READ, &hkRegistryDependents); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); } else { @@ -515,11 +530,11 @@ DAPI_(HRESULT) DepUnregisterDependent( // Delete the wzProviderKey dependent sub-key. hr = RegDelete(hkRegistryDependents, wzProviderKey, REG_KEY_DEFAULT, TRUE); - ExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); // If there are no remaining dependents, delete the Dependents subkey. hr = RegQueryKey(hkRegistryDependents, &cSubKeys, NULL); - ExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey); if (0 < cSubKeys) { @@ -531,11 +546,11 @@ DAPI_(HRESULT) DepUnregisterDependent( // Fail if there are any subkeys since we just checked. hr = RegDelete(hkDependencyProviderKey, vsczRegistryDependents, REG_KEY_DEFAULT, FALSE); - ExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); // If there are no values, delete the provider dependency key. hr = RegQueryKey(hkDependencyProviderKey, NULL, &cValues); - ExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey); if (0 == cValues) { @@ -544,7 +559,7 @@ DAPI_(HRESULT) DepUnregisterDependent( // Fail if there are any subkeys since we just checked. hr = RegDelete(hkRegistryRoot, wzDependencyProviderKey, REG_KEY_DEFAULT, FALSE); - ExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey); + DepExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey); } LExit: @@ -567,21 +582,21 @@ DAPI_(HRESULT) DepDependencyArrayAlloc( DEPENDENCY* pDependency = NULL; hr = ::UIntAdd(*pcDependencies, 1, &cRequired); - ExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array."); + DepExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array."); hr = MemEnsureArraySize(reinterpret_cast(prgDependencies), cRequired, sizeof(DEPENDENCY), ARRAY_GROWTH_SIZE); - ExitOnFailure(hr, "Failed to allocate memory for the dependency array."); + DepExitOnFailure(hr, "Failed to allocate memory for the dependency array."); pDependency = static_cast(&(*prgDependencies)[*pcDependencies]); - ExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid."); + DepExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid."); hr = StrAllocString(&(pDependency->sczKey), wzKey, 0); - ExitOnFailure(hr, "Failed to allocate the string key in the dependency array."); + DepExitOnFailure(hr, "Failed to allocate the string key in the dependency array."); if (wzName) { hr = StrAllocString(&(pDependency->sczName), wzName, 0); - ExitOnFailure(hr, "Failed to allocate the string name in the dependency array."); + DepExitOnFailure(hr, "Failed to allocate the string name in the dependency array."); } // Update the number of current elements in the dependency array. @@ -623,18 +638,18 @@ static HRESULT AllocDependencyKeyName( // Get the length of the dependency, and add to the length of the root. hr = ::StringCchLengthW(wzName, STRSAFE_MAX_CCH, &cchName); - ExitOnFailure(hr, "Failed to get string length of dependency name."); + DepExitOnFailure(hr, "Failed to get string length of dependency name."); // Add the sizes together to allocate memory once (callee will add space for nul). hr = ::SizeTAdd(cchRegistryRoot, cchName, &cchKeyName); - ExitOnFailure(hr, "Failed to add the string lengths together."); + DepExitOnFailure(hr, "Failed to add the string lengths together."); // Allocate and concat the strings together. hr = StrAllocString(psczKeyName, vsczRegistryRoot, cchKeyName); - ExitOnFailure(hr, "Failed to allocate string for dependency registry root."); + DepExitOnFailure(hr, "Failed to allocate string for dependency registry root."); hr = StrAllocConcat(psczKeyName, wzName, cchName); - ExitOnFailure(hr, "Failed to concatenate the dependency key name."); + DepExitOnFailure(hr, "Failed to concatenate the dependency key name."); LExit: return hr; @@ -656,13 +671,13 @@ static HRESULT GetDependencyNameFromKey( // Format the provider dependency registry key. hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - ExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); // Try to open the dependency key. hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); } else { @@ -673,7 +688,7 @@ static HRESULT GetDependencyNameFromKey( hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); if (E_FILENOTFOUND != hr) { - ExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey); + DepExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey); } else { diff --git a/src/dutil/dictutil.cpp b/src/dutil/dictutil.cpp index 1f0f9e43..0d0743eb 100644 --- a/src/dutil/dictutil.cpp +++ b/src/dutil/dictutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define DictExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) +#define DictExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) +#define DictExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) +#define DictExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) +#define DictExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DICTUTIL, e, x, s, __VA_ARGS__) +#define DictExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DICTUTIL, g, x, s, __VA_ARGS__) + // These should all be primes, and spaced reasonably apart (currently each is about 4x the last) const DWORD MAX_BUCKET_SIZES[] = { 503, @@ -61,7 +76,7 @@ static HRESULT StringHash( __in const STRINGDICT_STRUCT *psd, __in DWORD dwNumBuckets, __in_z LPCWSTR pszString, - __out LPDWORD pdwHash + __out DWORD *pdwHash ); static BOOL IsMatchExact( __in const STRINGDICT_STRUCT *psd, @@ -122,11 +137,11 @@ extern "C" HRESULT DAPI DictCreateWithEmbeddedKey( { HRESULT hr = S_OK; - ExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); // Allocate the handle *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); - ExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); STRINGDICT_STRUCT *psd = static_cast(*psdHandle); @@ -151,7 +166,7 @@ extern "C" HRESULT DAPI DictCreateWithEmbeddedKey( // Finally, allocate our initial buckets psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); - ExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); LExit: return hr; @@ -166,11 +181,11 @@ extern "C" HRESULT DAPI DictCreateStringList( { HRESULT hr = S_OK; - ExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); // Allocate the handle *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); - ExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); STRINGDICT_STRUCT *psd = static_cast(*psdHandle); @@ -195,7 +210,7 @@ extern "C" HRESULT DAPI DictCreateStringList( // Finally, allocate our initial buckets psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); - ExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); LExit: return hr; @@ -212,7 +227,7 @@ extern "C" HRESULT DAPI DictCreateStringListFromArray( STRINGDICT_HANDLE sd = NULL; hr = DictCreateStringList(&sd, cStringArray, dfFlags); - ExitOnFailure(hr, "Failed to create the string dictionary."); + DictExitOnFailure(hr, "Failed to create the string dictionary."); for (DWORD i = 0; i < cStringArray; ++i) { @@ -221,12 +236,12 @@ extern "C" HRESULT DAPI DictCreateStringListFromArray( hr = DictKeyExists(sd, wzKey); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to check the string dictionary."); + DictExitOnFailure(hr, "Failed to check the string dictionary."); } else { hr = DictAddKey(sd, wzKey); - ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey); + DictExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey); } } @@ -252,7 +267,7 @@ extern "C" HRESULT DAPI DictCompareStringListToArray( hr = DictKeyExists(sdStringList, rgwzStringArray[i]); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to check the string dictionary."); + DictExitOnFailure(hr, "Failed to check the string dictionary."); ExitFunction1(hr = S_OK); } } @@ -273,19 +288,19 @@ extern "C" HRESULT DAPI DictAddKey( DWORD dwIndex = 0; STRINGDICT_STRUCT *psd = static_cast(sdHandle); - ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); - ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict"); + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict"); if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); } if (DICT_STRING_LIST != psd->dtType) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType); + DictExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType); } if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) @@ -299,18 +314,18 @@ extern "C" HRESULT DAPI DictAddKey( hr = S_OK; } } - ExitOnFailure(hr, "Failed to grow dictionary"); + DictExitOnFailure(hr, "Failed to grow dictionary"); } hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex); - ExitOnFailure(hr, "Failed to get index to insert into"); + DictExitOnFailure(hr, "Failed to get index to insert into"); hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); - ExitOnFailure(hr, "Failed to resize list of items in dictionary"); + DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); ++psd->dwNumItems; hr = StrAllocString(reinterpret_cast(&(psd->ppvBuckets[dwIndex])), pszString, 0); - ExitOnFailure(hr, "Failed to allocate copy of string"); + DictExitOnFailure(hr, "Failed to allocate copy of string"); psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex]; @@ -330,23 +345,23 @@ extern "C" HRESULT DAPI DictAddValue( DWORD dwIndex = 0; STRINGDICT_STRUCT *psd = static_cast(sdHandle); - ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); - ExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict"); + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + DictExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict"); if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); } if (DICT_EMBEDDED_KEY != psd->dtType) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType); + DictExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType); } wzKey = GetKey(psd, pvValue); - ExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict"); + DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict"); if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) { @@ -359,14 +374,14 @@ extern "C" HRESULT DAPI DictAddValue( hr = S_OK; } } - ExitOnFailure(hr, "Failed to grow dictionary"); + DictExitOnFailure(hr, "Failed to grow dictionary"); } hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex); - ExitOnFailure(hr, "Failed to get index to insert into"); + DictExitOnFailure(hr, "Failed to get index to insert into"); hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); - ExitOnFailure(hr, "Failed to resize list of items in dictionary"); + DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); ++psd->dwNumItems; pvOffset = TranslateValueToOffset(psd, pvValue); @@ -385,15 +400,15 @@ extern "C" HRESULT DAPI DictGetValue( { HRESULT hr = S_OK; - ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); - ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); const STRINGDICT_STRUCT *psd = static_cast(sdHandle); if (DICT_EMBEDDED_KEY != psd->dtType) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType); + DictExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType); } hr = GetValue(psd, pszString, ppvValue); @@ -401,7 +416,7 @@ extern "C" HRESULT DAPI DictGetValue( { ExitFunction(); } - ExitOnFailure(hr, "Failed to call internal GetValue()"); + DictExitOnFailure(hr, "Failed to call internal GetValue()"); LExit: return hr; @@ -414,8 +429,8 @@ extern "C" HRESULT DAPI DictKeyExists( { HRESULT hr = S_OK; - ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); - ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); const STRINGDICT_STRUCT *psd = static_cast(sdHandle); @@ -425,7 +440,7 @@ extern "C" HRESULT DAPI DictKeyExists( { ExitFunction(); } - ExitOnFailure(hr, "Failed to call internal GetValue()"); + DictExitOnFailure(hr, "Failed to call internal GetValue()"); LExit: return hr; @@ -467,7 +482,7 @@ static HRESULT StringHash( if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) { hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0); - ExitOnFailure(hr, "Failed to convert the string to upper-case."); + DictExitOnFailure(hr, "Failed to convert the string to upper-case."); wzKey = sczNewKey; } @@ -522,17 +537,17 @@ static HRESULT GetValue( void *pvCandidateValue = NULL; DWORD dwIndex = 0; - ExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict"); - ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + DictExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); } hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); - ExitOnFailure(hr, "Failed to hash the string."); + DictExitOnFailure(hr, "Failed to hash the string."); DWORD dwIndexCandidate = dwOriginalIndexCandidate; @@ -553,7 +568,7 @@ static HRESULT GetValue( { ExitFunction(); } - ExitOnFailure(hr, "Failed to find index to get"); + DictExitOnFailure(hr, "Failed to find index to get"); if (NULL != ppvValue) { @@ -581,7 +596,7 @@ static HRESULT GetInsertIndex( DWORD dwOriginalIndexCandidate = 0; hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate); - ExitOnFailure(hr, "Failed to hash the string."); + DictExitOnFailure(hr, "Failed to hash the string."); DWORD dwIndexCandidate = dwOriginalIndexCandidate; @@ -604,7 +619,7 @@ static HRESULT GetInsertIndex( { // The dict table is full - this error seems to be a reasonably close match hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL); - ExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString); + DictExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString); } } @@ -626,11 +641,11 @@ static HRESULT GetIndex( if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); } hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); - ExitOnFailure(hr, "Failed to hash the string."); + DictExitOnFailure(hr, "Failed to hash the string."); DWORD dwIndexCandidate = dwOriginalIndexCandidate; @@ -704,18 +719,18 @@ static HRESULT GrowDictionary( } hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary"); + DictExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary"); ppvNewBuckets = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]); + DictExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]); for (DWORD i = 0; i < psd->dwNumItems; ++i) { wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i])); - ExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value"); + DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value"); hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex); - ExitOnFailure(hr, "Failed to get index to insert into"); + DictExitOnFailure(hr, "Failed to get index to insert into"); ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i]; } diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp index 5e22ee65..81130a8d 100644 --- a/src/dutil/dirutil.cpp +++ b/src/dutil/dirutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define DirExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) +#define DirExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) +#define DirExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) +#define DirExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) +#define DirExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DIRUTIL, e, x, s, __VA_ARGS__) +#define DirExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DIRUTIL, g, x, s, __VA_ARGS__) + + /******************************************************************* DirExists @@ -59,12 +74,12 @@ extern "C" HRESULT DAPI DirCreateTempPath( cch = ::GetTempPathW(countof(wzDir), wzDir); if (!cch || cch >= countof(wzDir)) { - ExitWithLastError(hr, "Failed to GetTempPath."); + DirExitWithLastError(hr, "Failed to GetTempPath."); } if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) { - ExitWithLastError(hr, "Failed to GetTempFileName."); + DirExitWithLastError(hr, "Failed to GetTempFileName."); } hr = ::StringCchCopyW(wzPath, cchPath, wzFile); @@ -111,12 +126,12 @@ extern "C" HRESULT DAPI DirEnsureExists( } // if there is no parent directory fail - ExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); + DirExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); *pwzLastSlash = L'\0'; // null terminate the parent path hr = DirEnsureExists(wzPath, psa); // recurse! *pwzLastSlash = L'\\'; // put the slash back - ExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); + DirExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); // try to create the directory now that all parents are created if (!::CreateDirectoryW(wzPath, psa)) @@ -197,7 +212,7 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( er = ERROR_PATH_NOT_FOUND; } hr = HRESULT_FROM_WIN32(er); - ExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); + DirExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); } if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) @@ -206,7 +221,7 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( { if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL)) { - ExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); + DirExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); } } @@ -217,18 +232,18 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( { if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) { - ExitWithLastError(hr, "Failed to get temp directory."); + DirExitWithLastError(hr, "Failed to get temp directory."); } } // Delete everything in this directory. hr = PathConcat(wzPath, L"*.*", &sczDelete); - ExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); + DirExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); hFind = ::FindFirstFileW(sczDelete, &wfd); if (INVALID_HANDLE_VALUE == hFind) { - ExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); + DirExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); } do @@ -243,18 +258,18 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( wfd.cFileName[MAX_PATH - 1] = L'\0'; hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); - ExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); + DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { hr = PathBackslashTerminate(&sczDelete); - ExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); + DirExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call if (FAILED(hr)) { // if we failed to delete a subdirectory, keep trying to finish any remaining files - ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); + ExitTraceSource(DUTIL_SOURCE_DIRUTIL, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); hr = S_OK; } } @@ -264,7 +279,7 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( { if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL)) { - ExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); + DirExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); } } @@ -274,7 +289,7 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( { if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) { - ExitWithLastError(hr, "Failed to get temp file to move to."); + DirExitWithLastError(hr, "Failed to get temp file to move to."); } // Try to move the file to the temp directory then schedule for delete, @@ -290,7 +305,7 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( } else { - ExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); + DirExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); } } } @@ -303,7 +318,7 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( } else { - ExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); + DirExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); } } @@ -318,13 +333,13 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( } } - ExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); + DirExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); } } else { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); + DirExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); } Assert(S_OK == hr); @@ -351,22 +366,22 @@ extern "C" HRESULT DAPI DirGetCurrent( if (psczCurrentDirectory && *psczCurrentDirectory) { hr = StrMaxLength(*psczCurrentDirectory, &cch); - ExitOnFailure(hr, "Failed to determine size of current directory."); + DirExitOnFailure(hr, "Failed to determine size of current directory."); } DWORD cchRequired = ::GetCurrentDirectoryW(static_cast(cch), 0 == cch ? NULL : *psczCurrentDirectory); if (0 == cchRequired) { - ExitWithLastError(hr, "Failed to get current directory."); + DirExitWithLastError(hr, "Failed to get current directory."); } else if (cch < cchRequired) { hr = StrAlloc(psczCurrentDirectory, cchRequired); - ExitOnFailure(hr, "Failed to allocate string for current directory."); + DirExitOnFailure(hr, "Failed to allocate string for current directory."); if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory)) { - ExitWithLastError(hr, "Failed to get current directory using allocated string."); + DirExitWithLastError(hr, "Failed to get current directory using allocated string."); } } @@ -387,7 +402,7 @@ extern "C" HRESULT DAPI DirSetCurrent( if (!::SetCurrentDirectoryW(wzDirectory)) { - ExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); + DirExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); } LExit: diff --git a/src/dutil/dlutil.cpp b/src/dutil/dlutil.cpp index 1b30f410..70155e6f 100644 --- a/src/dutil/dlutil.cpp +++ b/src/dutil/dlutil.cpp @@ -5,6 +5,21 @@ #include #include + +// Exit macros +#define DlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) +#define DlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) +#define DlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) +#define DlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) +#define DlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DLUTIL, e, x, s, __VA_ARGS__) +#define DlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DLUTIL, g, x, s, __VA_ARGS__) + static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024; static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL }; @@ -41,7 +56,7 @@ static HRESULT DownloadResource( static HRESULT AllocateRangeRequestHeader( __in DWORD64 dw64ResumeOffset, __in DWORD64 dw64ResourceLength, - __deref_out_z LPWSTR* psczHeader + __deref_inout_z LPWSTR* psczHeader ); static HRESULT WriteToFile( __in HINTERNET hUrl, @@ -126,10 +141,10 @@ extern "C" HRESULT DAPI DownloadUrl( // Copy the download source into a working variable to handle redirects then // open the internet session. hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); - ExitOnFailure(hr, "Failed to copy download source URL."); + DlExitOnFailure(hr, "Failed to copy download source URL."); hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); - ExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); + DlExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); @@ -143,14 +158,14 @@ extern "C" HRESULT DAPI DownloadUrl( // Get the resource size and creation time from the internet. hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated); - ExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl); + DlExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl); // Ignore failure to initialize resume because we will fall back to full download then // download. InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate); - ExitOnFailure(hr, "Failed to download URL: %ls", sczUrl); + DlExitOnFailure(hr, "Failed to download URL: %ls", sczUrl); // Cleanup the resume file because we successfully downloaded the whole file. if (sczResumePath && *sczResumePath) @@ -185,19 +200,19 @@ static HRESULT InitializeResume( *pdw64ResumeOffset = 0; hr = DownloadGetResumePath(wzDestinationPath, psczResumePath); - ExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); + DlExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hResumeFile) { - ExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath); + DlExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath); } do { if (!::ReadFile(hResumeFile, reinterpret_cast(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) { - ExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath); + DlExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath); } cbTotalReadResumeData += cbReadData; } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); @@ -233,7 +248,7 @@ static HRESULT GetResourceMetadata( LONGLONG llLength = 0; hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); - ExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl); + DlExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl); hr = InternetGetSizeByHandle(hUrl, &llLength); if (FAILED(hr)) @@ -286,12 +301,12 @@ static HRESULT DownloadResource( hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hPayloadFile) { - ExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath); + DlExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath); } // Allocate a memory block on a page boundary in case we want to do optimal writing. pbData = static_cast(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); - ExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); + DlExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); // Let's try downloading the file assuming that range requests are accepted. If range requests // are not supported we'll have to start over and accept the fact that we only get one shot @@ -300,13 +315,13 @@ static HRESULT DownloadResource( while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) { hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); - ExitOnFailure(hr, "Failed to allocate range request header."); + DlExitOnFailure(hr, "Failed to allocate range request header."); ReleaseNullInternet(hConnect); ReleaseNullInternet(hUrl); hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); - ExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl); + DlExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl); // If we didn't get the size of the resource from the initial "HEAD" request // then let's try to get the size from this "GET" request. @@ -335,7 +350,7 @@ static HRESULT DownloadResource( } hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache); - ExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); + DlExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); } LExit: @@ -354,7 +369,7 @@ LExit: static HRESULT AllocateRangeRequestHeader( __in DWORD64 dw64ResumeOffset, __in DWORD64 dw64ResourceLength, - __deref_out_z LPWSTR* psczHeader + __deref_inout_z LPWSTR* psczHeader ) { HRESULT hr = S_OK; @@ -368,7 +383,7 @@ static HRESULT AllocateRangeRequestHeader( if (0 < dw64ResumeOffset) { hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset); - ExitOnFailure(hr, "Failed to add range read header."); + DlExitOnFailure(hr, "Failed to add range read header."); } else { @@ -378,7 +393,7 @@ static HRESULT AllocateRangeRequestHeader( else // we'll have to download in chunks. { hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1); - ExitOnFailure(hr, "Failed to add range read header."); + DlExitOnFailure(hr, "Failed to add range read header."); } LExit: @@ -400,14 +415,14 @@ static HRESULT WriteToFile( DWORD cbReadData = 0; hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN); - ExitOnFailure(hr, "Failed to seek to start point in file."); + DlExitOnFailure(hr, "Failed to seek to start point in file."); do { // Read bits from the internet. if (!::InternetReadFile(hUrl, static_cast(pbData), cbData, &cbReadData)) { - ExitWithLastError(hr, "Failed while reading from internet."); + DlExitWithLastError(hr, "Failed while reading from internet."); } // Write bits to disk (if there are any). @@ -419,7 +434,7 @@ static HRESULT WriteToFile( { if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL)) { - ExitWithLastError(hr, "Failed to write data from internet."); + DlExitWithLastError(hr, "Failed to write data from internet."); } cbTotalWritten += cbWritten; @@ -431,7 +446,7 @@ static HRESULT WriteToFile( if (pCallback && pCallback->pfnProgress) { hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile); - ExitOnFailure(hr, "UX aborted on cache progress."); + DlExitOnFailure(hr, "UX aborted on cache progress."); } } } while (cbReadData); @@ -456,14 +471,14 @@ static HRESULT UpdateResumeOffset( DWORD cbWrittenResumeData = 0; hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN); - ExitOnFailure(hr, "Failed to seek to start point in file."); + DlExitOnFailure(hr, "Failed to seek to start point in file."); do { // Ignore failure to write to the resume file as that should not prevent the download from happening. if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL)) { - ExitOnFailure(hr, "Failed to seek to write to file."); + DlExitOnFailure(hr, "Failed to seek to write to file."); } cbTotalWrittenResumeData += cbWrittenResumeData; @@ -504,10 +519,10 @@ static HRESULT MakeRequest( // Open the url. hr = UriCrackEx(*psczSourceUrl, &uri); - ExitOnFailure(hr, "Failed to break URL into server and resource parts."); + DlExitOnFailure(hr, "Failed to break URL into server and resource parts."); 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); - ExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); + DlExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); // Best effort set the proxy username and password, if they were provided. if ((wzUser && *wzUser) && (wzPassword && *wzPassword)) @@ -519,10 +534,10 @@ static HRESULT MakeRequest( } hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl); - ExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl); + DlExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl); hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted); - ExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl); + DlExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl); } while (fRetry); // Okay, we're all ready to start downloading. Update the connection information. @@ -565,23 +580,23 @@ static HRESULT OpenRequest( // Allocate the resource name. hr = StrAllocString(&sczResource, wzResource, 0); - ExitOnFailure(hr, "Failed to allocate string for resource URI."); + DlExitOnFailure(hr, "Failed to allocate string for resource URI."); if (wzQueryString && *wzQueryString) { hr = StrAllocConcat(&sczResource, wzQueryString, 0); - ExitOnFailure(hr, "Failed to append query strong to resource from URI."); + DlExitOnFailure(hr, "Failed to append query strong to resource from URI."); } // Open the request and add the header if provided. hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL); - ExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); + DlExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); if (wzHeader && *wzHeader) { if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast(-1), HTTP_ADDREQ_FLAG_COALESCE)) { - ExitWithLastError(hr, "Failed to add header to HTTP request."); + DlExitWithLastError(hr, "Failed to add header to HTTP request."); } } @@ -618,12 +633,12 @@ static HRESULT SendRequest( // Try to get the HTTP status code and, if good, handle via the switch statement below but if it // fails return the error code from the send request above as the result of the function. HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); - ExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); + DlExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); } else // get the http status code. { hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); - ExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); + DlExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); } switch (lCode) @@ -643,7 +658,7 @@ static HRESULT SendRequest( case 302: __fallthrough; // temporary case 303: // redirect method hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl); - ExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl); + DlExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl); *pfRetry = TRUE; break; @@ -734,7 +749,7 @@ static HRESULT DownloadGetResumePath( HRESULT hr = S_OK; hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); - ExitOnFailure(hr, "Failed to create resume path."); + DlExitOnFailure(hr, "Failed to create resume path."); LExit: return hr; @@ -769,7 +784,7 @@ static HRESULT DownloadSendProgressCallback( case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? case PROGRESS_STOP: hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - ExitOnRootFailure(hr, "UX aborted on download progress."); + DlExitOnRootFailure(hr, "UX aborted on download progress."); case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. pCallback->pfnProgress = NULL; @@ -778,7 +793,7 @@ static HRESULT DownloadSendProgressCallback( default: hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Invalid return code from progress routine."); + DlExitOnRootFailure(hr, "Invalid return code from progress routine."); } } diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp index 99ce1bc6..c500191a 100644 --- a/src/dutil/dutil.cpp +++ b/src/dutil/dutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) +#define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) +#define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) +#define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) +#define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__) +#define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__) + // No need for OACR to warn us about using non-unicode APIs in this file. #pragma prefast(disable:25068) @@ -84,7 +99,7 @@ extern "C" void DAPI Dutil_AssertMsg( char szMsg[DUTIL_STRING_BUFFER]; hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); - ExitOnFailure(hr, "failed to copy message while building assert message"); + DExitOnFailure(hr, "failed to copy message while building assert message"); if (Dutil_pfnDisplayAssert) { @@ -123,7 +138,7 @@ extern "C" void DAPI Dutil_AssertMsg( if (ERROR_SUCCESS != er) { hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); - ExitOnFailure(hr, "failed to concat string while building assert message"); + DExitOnFailure(hr, "failed to concat string while building assert message"); id = ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | @@ -480,24 +495,24 @@ extern "C" HRESULT DAPI LoadSystemLibraryWithPath( WCHAR wzPath[MAX_PATH] = { }; cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); - ExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); + DExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); if (L'\\' != wzPath[cch - 1]) { hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); - ExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); + DExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); } hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); - ExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); + DExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); *phModule = ::LoadLibraryW(wzPath); - ExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); + DExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); if (psczPath) { hr = StrAllocString(psczPath, wzPath, MAX_PATH); - ExitOnFailure(hr, "Failed to copy the path to library."); + DExitOnFailure(hr, "Failed to copy the path to library."); } LExit: diff --git a/src/dutil/eseutil.cpp b/src/dutil/eseutil.cpp index 599a3943..d2bd7dc5 100644 --- a/src/dutil/eseutil.cpp +++ b/src/dutil/eseutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define EseExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) +#define EseExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) +#define EseExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) +#define EseExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) +#define EseExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ESEUTIL, e, x, s, __VA_ARGS__) +#define EseExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ESEUTIL, g, x, s, __VA_ARGS__) + struct ESE_QUERY { ESE_QUERY_TYPE qtQueryType; @@ -85,13 +100,13 @@ HRESULT HresultFromJetError(JET_ERR jEr) } // 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 - ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Encountered Jet Error: 0x%08x", jEr); + ExitTraceSource(DUTIL_SOURCE_ESEUTIL, hr, "Encountered Jet Error: 0x%08x", jEr); return hr; } -#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__); goto LExit; }} -#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__); goto LExit; }} +#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }} +#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; }} HRESULT DAPI EseBeginSession( __out JET_INSTANCE *pjiInstance, @@ -106,15 +121,15 @@ HRESULT DAPI EseBeginSession( LPSTR pszAnsiPath = NULL; hr = DirEnsureExists(pszPath, NULL); - ExitOnFailure(hr, "Failed to ensure database directory exists"); + EseExitOnFailure(hr, "Failed to ensure database directory exists"); // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, // likely breaking everyone with unicode characters in their path. hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting instance name to ansi"); + EseExitOnFailure(hr, "Failed converting instance name to ansi"); hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting session path name to ansi"); + EseExitOnFailure(hr, "Failed converting session path name to ansi"); jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance); ExitOnJetFailure(jEr, hr, "Failed to create instance"); @@ -173,17 +188,17 @@ HRESULT AllocColumnCreateStruct( size_t cbAllocSize = 0; hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize)); - ExitOnFailure(hr, "Maximum allocation exceeded."); + EseExitOnFailure(hr, "Maximum allocation exceeded."); *ppjccColumnCreate = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database"); + EseExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database"); for (i = 0; i < ptsSchema->dwColumns; ++i) { (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE); hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); - ExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName); + EseExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName); (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType; @@ -237,7 +252,7 @@ HRESULT FreeColumnCreateStruct( } hr = MemFree(pjccColumnCreate); - ExitOnFailure(hr, "Failed to release core column create struct"); + EseExitOnFailure(hr, "Failed to release core column create struct"); LExit: return hr; @@ -261,20 +276,20 @@ HRESULT AllocIndexCreateStruct( if (ptsSchema->pcsColumns[i].fKey) { hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); - ExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName); + EseExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName); hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0); - ExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString); + EseExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString); hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0); - ExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString); + EseExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString); ReleaseNullStr(pszTempString); // All question marks will be converted to null characters later; this is just to trick dutil // into letting us create an ansi, double-null-terminated list of single-null-terminated strings hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0); - ExitOnFailure(hr, "Failed to append placeholder character to multisz string: %ls", pszMultiSzKeys); + EseExitOnFailure(hr, "Failed to append placeholder character to multisz string: %hs", pszMultiSzKeys); // Record that at least one key column was found fKeyColumns = TRUE; @@ -288,18 +303,18 @@ HRESULT AllocIndexCreateStruct( } hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP); - ExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName); + EseExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName); hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0); - ExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName); + EseExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName); *ppjicIndexCreate = static_cast(MemAlloc(sizeof(JET_INDEXCREATE), TRUE)); - ExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); + EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); // Record the size including both null terminators - the struct requires this DWORD dwSize = 0; dwSize = lstrlen(pszMultiSzKeys) + 1; // add 1 to include null character at the end - ExitOnFailure(hr, "Failed to get size of keys string"); + EseExitOnFailure(hr, "Failed to get size of keys string"); // At this point convert all question marks to null characters for (i = 0; i < dwSize; ++i) @@ -349,7 +364,7 @@ HRESULT EnsureSchema( jtTableCreate.cIndexes = 1; hr = EseBeginTransaction(jsSession); - ExitOnFailure(hr, "Failed to begin transaction to create tables"); + EseExitOnFailure(hr, "Failed to begin transaction to create tables"); fTransaction = TRUE; for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable) @@ -363,13 +378,13 @@ HRESULT EnsureSchema( { // Fill out the JET_TABLECREATE struct hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting table name to ansi"); + EseExitOnFailure(hr, "Failed converting table name to ansi"); hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate); - ExitOnFailure(hr, "Failed to allocate column create struct"); + EseExitOnFailure(hr, "Failed to allocate column create struct"); hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate); - ExitOnFailure(hr, "Failed to allocate index create struct"); + EseExitOnFailure(hr, "Failed to allocate index create struct"); jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns; jtTableCreate.tableid = NULL; @@ -392,7 +407,7 @@ HRESULT EnsureSchema( ReleaseNullStr(jtTableCreate.szTableName); hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); - ExitOnFailure(hr, "Failed to free column create struct"); + EseExitOnFailure(hr, "Failed to free column create struct"); jtTableCreate.rgcolumncreate = NULL; } else @@ -422,7 +437,7 @@ HRESULT EnsureSchema( } hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn); - ExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName); + EseExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName); } } } @@ -464,13 +479,13 @@ HRESULT DAPI EseEnsureDatabase( // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, // likely breaking all those with unicode characters in their path. hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting database name to ansi"); + EseExitOnFailure(hr, "Failed converting database name to ansi"); hr = PathGetDirectory(pszFile, &pszDir); - ExitOnFailure(hr, "Failed to get directory that will contain database file"); + EseExitOnFailure(hr, "Failed to get directory that will contain database file"); hr = DirEnsureExists(pszDir, NULL); - ExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir); + EseExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir); if (FileExistsEx(pszFile, NULL)) { @@ -498,7 +513,7 @@ HRESULT DAPI EseEnsureDatabase( } hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema); - ExitOnFailure(hr, "Failed to ensure database schema matches expectations"); + EseExitOnFailure(hr, "Failed to ensure database schema matches expectations"); LExit: ReleaseStr(pszDir); @@ -535,7 +550,7 @@ HRESULT DAPI EseCreateTable( LPSTR pszAnsiTable = NULL; hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting table name to ansi"); + EseExitOnFailure(hr, "Failed converting table name to ansi"); jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable); ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable); @@ -558,7 +573,7 @@ HRESULT DAPI EseOpenTable( LPSTR pszAnsiTable = NULL; hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting table name to ansi"); + EseExitOnFailure(hr, "Failed converting table name to ansi"); jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable); ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable); @@ -602,7 +617,7 @@ HRESULT DAPI EseEnsureColumn( JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting column name to ansi"); + EseExitOnFailure(hr, "Failed converting column name to ansi"); jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); if (JET_errSuccess == jEr) @@ -661,7 +676,7 @@ HRESULT DAPI EseGetColumn( JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); - ExitOnFailure(hr, "Failed converting column name to ansi"); + EseExitOnFailure(hr, "Failed converting column name to ansi"); jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); if (JET_errSuccess == jEr) @@ -898,7 +913,7 @@ HRESULT DAPI EseGetColumnBinary( __in JET_SESID jsSession, __in ESE_TABLE_SCHEMA tsTable, __in DWORD dwColumn, - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer ) { @@ -916,12 +931,12 @@ HRESULT DAPI EseGetColumnBinary( if (NULL == *ppbBuffer) { *ppbBuffer = reinterpret_cast(MemAlloc(ulActualSize, FALSE)); - ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column"); + EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column"); } else { *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, ulActualSize, FALSE)); - ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column"); + EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column"); } jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL); @@ -1001,7 +1016,7 @@ HRESULT DAPI EseGetColumnString( ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record"); hr = StrAlloc(ppszValue, ulActualSize); - ExitOnFailure(hr, "Failed to allocate string while retrieving column value"); + EseExitOnFailure(hr, "Failed to allocate string while retrieving column value"); jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL); ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record"); @@ -1023,7 +1038,7 @@ HRESULT DAPI EseBeginQuery( HRESULT hr = S_OK; *peqhHandle = static_cast(MemAlloc(sizeof(ESE_QUERY), TRUE)); - ExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query"); + EseExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query"); ESE_QUERY *peqHandle = static_cast(*peqhHandle); peqHandle->qtQueryType = qtQueryType; @@ -1050,7 +1065,7 @@ HRESULT DAPI SetQueryColumn( if (peqHandle->dwColumns == countof(peqHandle->pvData)) { hr = E_NOTIMPL; - ExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData)); + EseExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData)); } if (0 == peqHandle->dwColumns) // If it's the first column, start a new key @@ -1065,7 +1080,7 @@ HRESULT DAPI SetQueryColumn( if (ESE_QUERY_EXACT != peqHandle->qtQueryType) { peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE); - ExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory"); + EseExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory"); memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData); @@ -1108,7 +1123,7 @@ HRESULT DAPI EseSetQueryColumnBinary( } hr = SetQueryColumn(eqhHandle, reinterpret_cast(pbBuffer), static_cast(cbBuffer), jGrb); - ExitOnFailure(hr, "Failed to set value of query colum (as binary) to:"); + EseExitOnFailure(hr, "Failed to set value of query colum (as binary) to:"); LExit: return hr; @@ -1137,7 +1152,7 @@ HRESULT DAPI EseSetQueryColumnDword( } hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb); - ExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData); + EseExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData); LExit: return hr; @@ -1167,7 +1182,7 @@ HRESULT DAPI EseSetQueryColumnBool( } hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb); - ExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE"); + EseExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE"); LExit: return hr; @@ -1200,7 +1215,7 @@ HRESULT DAPI EseSetQueryColumnString( } hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb); - ExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString); + EseExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString); LExit: return hr; diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp index 6191ec06..c76017de 100644 --- a/src/dutil/fileutil.cpp +++ b/src/dutil/fileutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__) +#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__) + // constants const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; @@ -15,7 +30,7 @@ const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations ********************************************************************/ extern "C" LPWSTR DAPI FileFromPath( - __in LPCWSTR wzPath + __in_z LPCWSTR wzPath ) { if (!wzPath) @@ -42,7 +57,7 @@ extern "C" LPWSTR DAPI FileFromPath( ********************************************************************/ extern "C" HRESULT DAPI FileResolvePath( - __in LPCWSTR wzRelativePath, + __in_z LPCWSTR wzRelativePath, __out LPWSTR *ppwzFullPath ) { @@ -63,28 +78,28 @@ extern "C" HRESULT DAPI FileResolvePath( // cchExpandedPath = MAX_PATH; hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); - ExitOnFailure(hr, "Failed to allocate space for expanded path."); + FileExitOnFailure(hr, "Failed to allocate space for expanded path."); cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); if (0 == cch) { - ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); } else if (cchExpandedPath < cch) { cchExpandedPath = cch; hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); - ExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + FileExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); if (0 == cch) { - ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); } else if (cchExpandedPath < cch) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + FileExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); } } @@ -93,28 +108,28 @@ extern "C" HRESULT DAPI FileResolvePath( // cchFullPath = MAX_PATH; hr = StrAlloc(&pwzFullPath, cchFullPath); - ExitOnFailure(hr, "Failed to allocate space for full path."); + FileExitOnFailure(hr, "Failed to allocate space for full path."); cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); if (0 == cch) { - ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); } else if (cchFullPath < cch) { cchFullPath = cch; hr = StrAlloc(&pwzFullPath, cchFullPath); - ExitOnFailure(hr, "Failed to re-allocate more space for full path."); + FileExitOnFailure(hr, "Failed to re-allocate more space for full path."); cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); if (0 == cch) { - ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); } else if (cchFullPath < cch) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + FileExitOnRootFailure(hr, "Failed to allocate buffer for full path."); } } @@ -133,7 +148,7 @@ LExit: FileStripExtension - Strip extension from filename ********************************************************************/ extern "C" HRESULT DAPI FileStripExtension( -__in LPCWSTR wzFileName, +__in_z LPCWSTR wzFileName, __out LPWSTR *ppwzFileNameNoExtension ) { @@ -158,14 +173,14 @@ __out LPWSTR *ppwzFileNameNoExtension } hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); - ExitOnFailure(hr, "failed to allocate space for File Name without extension"); + FileExitOnFailure(hr, "failed to allocate space for File Name without extension"); // _wsplitpath_s can handle drive/path/filename/extension errno_t err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); if (0 != err) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to parse filename: %ls", wzFileName); + FileExitOnFailure(hr, "failed to parse filename: %ls", wzFileName); } *ppwzFileNameNoExtension = pwzFileNameNoExtension; @@ -182,8 +197,8 @@ LExit: FileChangeExtension - Changes the extension of a filename ********************************************************************/ extern "C" HRESULT DAPI FileChangeExtension( - __in LPCWSTR wzFileName, - __in LPCWSTR wzNewExtension, + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzNewExtension, __out LPWSTR *ppwzFileNameNewExtension ) { @@ -193,10 +208,10 @@ extern "C" HRESULT DAPI FileChangeExtension( LPWSTR sczFileName = NULL; hr = FileStripExtension(wzFileName, &sczFileName); - ExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); + FileExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); hr = StrAllocConcat(&sczFileName, wzNewExtension, 0); - ExitOnFailure(hr, "Failed to add new extension."); + FileExitOnFailure(hr, "Failed to add new extension."); *ppwzFileNameNewExtension = sczFileName; sczFileName = NULL; @@ -238,11 +253,11 @@ extern "C" HRESULT DAPI FileAddSuffixToBaseName( { // no extension, so add the suffix at the end of the whole name hr = StrAllocString(&sczNewFileName, wzFileName, 0); - ExitOnFailure(hr, "Failed to allocate new file name."); + FileExitOnFailure(hr, "Failed to allocate new file name."); hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0); } - ExitOnFailure(hr, "Failed to allocate new file name with suffix."); + FileExitOnFailure(hr, "Failed to allocate new file name with suffix."); *psczNewFileName = sczNewFileName; sczNewFileName = NULL; @@ -259,7 +274,7 @@ LExit: ********************************************************************/ extern "C" HRESULT DAPI FileVersion( - __in LPCWSTR wzFilename, + __in_z LPCWSTR wzFilename, __out DWORD *pdwVerMajor, __out DWORD* pdwVerMinor ) @@ -274,20 +289,20 @@ extern "C" HRESULT DAPI FileVersion( if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle))) { - ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); } pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer); - ExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); + FileExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer)) { - ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); } if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo)) { - ExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); + FileExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); } *pdwVerMajor = pvsFileInfo->dwFileVersionMS; @@ -307,7 +322,7 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI FileVersionFromString( - __in LPCWSTR wzVersion, + __in_z LPCWSTR wzVersion, __out DWORD* pdwVerMajor, __out DWORD* pdwVerMinor ) @@ -394,7 +409,7 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI FileVersionFromStringEx( - __in LPCWSTR wzVersion, + __in_z LPCWSTR wzVersion, __in DWORD cchVersion, __out DWORD64* pqwVersion ) @@ -453,11 +468,11 @@ extern "C" HRESULT DAPI FileVersionFromStringEx( DWORD cchPart; hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); - ExitOnFailure(hr, "Version number part was too long."); + FileExitOnFailure(hr, "Version number part was too long."); // parse version part hr = StrStringToUInt16(wzPartBegin, cchPart, &us); - ExitOnFailure(hr, "Failed to parse version number part."); + FileExitOnFailure(hr, "Failed to parse version number part."); // add part to qword version qwVersion |= (DWORD64)us << ((3 - iPart) * 16); @@ -501,7 +516,7 @@ extern "C" HRESULT DAPI FileVersionToStringEx( // Format and return the version string. hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision); - ExitOnFailure(hr, "Failed to allocate and format the version number."); + FileExitOnFailure(hr, "Failed to allocate and format the version number."); LExit: return hr; @@ -527,7 +542,7 @@ extern "C" HRESULT DAPI FileSetPointer( liMove.QuadPart = dw64Move; if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) { - ExitWithLastError(hr, "Failed to set file pointer."); + FileExitWithLastError(hr, "Failed to set file pointer."); } if (pdw64NewPosition) @@ -545,23 +560,23 @@ LExit: ********************************************************************/ extern "C" HRESULT DAPI FileSize( - __in LPCWSTR pwzFileName, + __in_z LPCWSTR pwzFileName, __out LONGLONG* pllSize ) { HRESULT hr = S_OK; HANDLE hFile = INVALID_HANDLE_VALUE; - ExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); + FileExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (INVALID_HANDLE_VALUE == hFile) { - ExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); + FileExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); } hr = FileSizeByHandle(hFile, pllSize); - ExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); + FileExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); LExit: ReleaseFileHandle(hFile); @@ -587,7 +602,7 @@ extern "C" HRESULT DAPI FileSizeByHandle( if (!::GetFileSizeEx(hFile, &li)) { - ExitWithLastError(hr, "Failed to get size of file."); + FileExitWithLastError(hr, "Failed to get size of file."); } *pllSize = li.QuadPart; @@ -602,7 +617,7 @@ LExit: ********************************************************************/ extern "C" BOOL DAPI FileExistsEx( - __in LPCWSTR wzPath, + __in_z LPCWSTR wzPath, __out_opt DWORD *pdwAttributes ) { @@ -655,14 +670,14 @@ extern "C" BOOL DAPI FileExistsAfterRestart( { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to open pending file rename registry key."); + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); if (E_FILENOTFOUND == hr) { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to read pending file renames."); + FileExitOnFailure(hr, "Failed to read pending file renames."); // The pending file renames array is pairs of source and target paths. We only care // about checking the source paths so skip the target paths (i += 2). @@ -678,7 +693,7 @@ extern "C" BOOL DAPI FileExistsAfterRestart( } hr = PathCompare(wzPath, wzRename, &nCompare); - ExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); if (CSTR_EQUAL == nCompare) { @@ -719,14 +734,14 @@ extern "C" HRESULT DAPI FileRemoveFromPendingRename( { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to open pending file rename registry key."); + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); if (E_FILENOTFOUND == hr) { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to read pending file renames."); + FileExitOnFailure(hr, "Failed to read pending file renames."); // The pending file renames array is pairs of source and target paths. We only care // about checking the source paths so skip the target paths (i += 2). @@ -742,7 +757,7 @@ extern "C" HRESULT DAPI FileRemoveFromPendingRename( } hr = PathCompare(wzPath, wzRename, &nCompare); - ExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); // If we find our path in the list, null out the source and target slot and // we'll compact the array next. @@ -772,7 +787,7 @@ extern "C" HRESULT DAPI FileRemoveFromPendingRename( // Write the new array back to the pending file rename key. hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); - ExitOnFailure(hr, "Failed to update pending file renames."); + FileExitOnFailure(hr, "Failed to update pending file renames."); } LExit: @@ -790,7 +805,7 @@ LExit: extern "C" HRESULT DAPI FileRead( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, __out SIZE_T* pcbDest, - __in LPCWSTR wzSrcPath + __in_z LPCWSTR wzSrcPath ) { HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE); @@ -819,7 +834,7 @@ extern "C" HRESULT DAPI FileReadEx( extern "C" HRESULT DAPI FileReadUntil( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in LPCWSTR wzSrcPath, + __in_z LPCWSTR wzSrcPath, __in DWORD cbMaxRead ) { @@ -835,7 +850,7 @@ extern "C" HRESULT DAPI FileReadUntil( extern "C" HRESULT DAPI FileReadPartial( __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in LPCWSTR wzSrcPath, + __in_z LPCWSTR wzSrcPath, __in BOOL fSeek, __in DWORD cbStartPosition, __in DWORD cbMaxRead, @@ -850,7 +865,7 @@ extern "C" HRESULT DAPI FileReadPartial( (with specified share mode) ********************************************************************/ extern "C" HRESULT DAPI FileReadPartialEx( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in BOOL fSeek, @@ -868,10 +883,10 @@ extern "C" HRESULT DAPI FileReadPartialEx( DWORD cbData = 0; BYTE* pbData = NULL; - ExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); - ExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); - ExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); - ExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); + FileExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); + FileExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); + FileExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); + FileExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) @@ -881,12 +896,12 @@ extern "C" HRESULT DAPI FileReadPartialEx( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); + FileExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); } if (!::GetFileSizeEx(hFile, &liFileSize)) { - ExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); + FileExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); } if (fSeek) @@ -894,13 +909,13 @@ extern "C" HRESULT DAPI FileReadPartialEx( if (cbStartPosition > liFileSize.QuadPart) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Start position %d bigger than file '%ls' size %d", cbStartPosition, wzSrcPath, liFileSize.QuadPart); + FileExitOnFailure(hr, "Start position %d bigger than file '%ls' size %llu", cbStartPosition, wzSrcPath, liFileSize.QuadPart); } DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT); if (INVALID_SET_FILE_POINTER == dwErr) { - ExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); + FileExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); } } else @@ -918,7 +933,7 @@ extern "C" HRESULT DAPI FileReadPartialEx( if (cbMaxRead < liFileSize.QuadPart - cbStartPosition) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); + FileExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); } } @@ -932,7 +947,7 @@ extern "C" HRESULT DAPI FileReadPartialEx( } LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); + FileExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); pbData = static_cast(pv); } @@ -945,7 +960,7 @@ extern "C" HRESULT DAPI FileReadPartialEx( } pbData = static_cast(MemAlloc(cbData, TRUE)); - ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); + FileExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); } DWORD cbTotalRead = 0; @@ -954,11 +969,11 @@ extern "C" HRESULT DAPI FileReadPartialEx( { DWORD cbRemaining = 0; hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining); - ExitOnFailure(hr, "Underflow calculating remaining buffer size."); + FileExitOnFailure(hr, "Underflow calculating remaining buffer size."); if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL)) { - ExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); + FileExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); } cbTotalRead += cbRead; @@ -967,7 +982,7 @@ extern "C" HRESULT DAPI FileReadPartialEx( if (cbTotalRead != cbData) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); + FileExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); } *ppbDest = pbData; @@ -999,10 +1014,10 @@ extern "C" HRESULT DAPI FileWrite( // Open the file hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); - ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); hr = FileWriteHandle(hFile, pbData, cbData); - ExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); + FileExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); if (pHandle) { @@ -1036,7 +1051,7 @@ extern "C" HRESULT DAPI FileWriteHandle( { if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)(cbData - cbTotal), &cbDataWritten, NULL)) { - ExitOnLastError(hr, "Failed to write data to file handle."); + FileExitOnLastError(hr, "Failed to write data to file handle."); } cbTotal += cbDataWritten; @@ -1068,13 +1083,13 @@ extern "C" HRESULT DAPI FileCopyUsingHandles( cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) { - ExitWithLastError(hr, "Failed to read from source."); + FileExitWithLastError(hr, "Failed to read from source."); } if (cbRead) { hr = FileWriteHandle(hTarget, rgbData, cbRead); - ExitOnFailure(hr, "Failed to write to target."); + FileExitOnFailure(hr, "Failed to write to target."); } cbTotalCopied += cbRead; @@ -1095,8 +1110,8 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI FileEnsureCopy( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, __in BOOL fOverwrite ) { @@ -1132,12 +1147,12 @@ extern "C" HRESULT DAPI FileEnsureCopy( *pwzLastSlash = L'\0'; // null terminate hr = DirEnsureExists(wzTarget, NULL); *pwzLastSlash = L'\\'; // now put the slash back - ExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); + FileExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); // try to copy again if (!::CopyFileW(wzSource, wzTarget, fOverwrite)) { - ExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); + FileExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); } } else // no path was specified so just return the error @@ -1186,7 +1201,7 @@ extern "C" HRESULT DAPI FileEnsureCopyWithRetry( break; // no reason to retry these errors. } } - ExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + FileExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); LExit: return hr; @@ -1198,8 +1213,8 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI FileEnsureMove( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, __in BOOL fOverwrite, __in BOOL fAllowCopy ) @@ -1260,12 +1275,12 @@ extern "C" HRESULT DAPI FileEnsureMove( *pwzLastSlash = L'\0'; // null terminate hr = DirEnsureExists(wzTarget, NULL); *pwzLastSlash = L'\\'; // now put the slash back - ExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); + FileExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); // try to move again if (!::MoveFileExW(wzSource, wzTarget, dwFlags)) { - ExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); + FileExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); } } else // no path was specified so just return the error @@ -1310,7 +1325,7 @@ extern "C" HRESULT DAPI FileEnsureMoveWithRetry( hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy); } - ExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + FileExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); LExit: return hr; @@ -1323,8 +1338,8 @@ LExit: NOTE: uses ANSI functions internally so it is Win9x safe ********************************************************************/ extern "C" HRESULT DAPI FileCreateTemp( - __in LPCWSTR wzPrefix, - __in LPCWSTR wzExtension, + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, __deref_opt_out_z LPWSTR* ppwzTempFile, __out_opt HANDLE* phTempFile ) @@ -1340,13 +1355,13 @@ extern "C" HRESULT DAPI FileCreateTemp( int i = 0; hr = StrAnsiAlloc(&pszTempPath, cchTempPath); - ExitOnFailure(hr, "failed to allocate memory for the temp path"); + FileExitOnFailure(hr, "failed to allocate memory for the temp path"); ::GetTempPathA(cchTempPath, pszTempPath); for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) { hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension); - ExitOnFailure(hr, "failed to allocate memory for log file"); + FileExitOnFailure(hr, "failed to allocate memory for log file"); hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hTempFile) @@ -1358,7 +1373,7 @@ extern "C" HRESULT DAPI FileCreateTemp( hr = S_OK; continue; } - ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pszTempFile); + FileExitOnFailureDebugTrace(hr, "failed to create file: %hs", pszTempFile); } } @@ -1387,8 +1402,8 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI FileCreateTempW( - __in LPCWSTR wzPrefix, - __in LPCWSTR wzExtension, + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, __deref_opt_out_z LPWSTR* ppwzTempFile, __out_opt HANDLE* phTempFile ) @@ -1405,13 +1420,13 @@ extern "C" HRESULT DAPI FileCreateTempW( if (!::GetTempPathW(cchTempPath, wzTempPath)) { - ExitOnLastError(hr, "failed to get temp path"); + FileExitOnLastError(hr, "failed to get temp path"); } for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) { hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); - ExitOnFailure(hr, "failed to allocate memory for temp filename"); + FileExitOnFailure(hr, "failed to allocate memory for temp filename"); hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hTempFile) @@ -1423,7 +1438,7 @@ extern "C" HRESULT DAPI FileCreateTempW( hr = S_OK; continue; } - ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); + FileExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); } } @@ -1452,8 +1467,8 @@ LExit: ********************************************************************/ extern "C" HRESULT DAPI FileIsSame( - __in LPCWSTR wzFile1, - __in LPCWSTR wzFile2, + __in_z LPCWSTR wzFile1, + __in_z LPCWSTR wzFile2, __out LPBOOL lpfSameFile ) { @@ -1464,19 +1479,19 @@ extern "C" HRESULT DAPI FileIsSame( BY_HANDLE_FILE_INFORMATION fileInfo2 = { }; hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - ExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); + FileExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - ExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); + FileExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); if (!::GetFileInformationByHandle(hFile1, &fileInfo1)) { - ExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); + FileExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); } if (!::GetFileInformationByHandle(hFile2, &fileInfo2)) { - ExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); + FileExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); } *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber && @@ -1495,7 +1510,7 @@ LExit: hidden, or system attributes if necessary. ********************************************************************/ extern "C" HRESULT DAPI FileEnsureDelete( - __in LPCWSTR wzFile + __in_z LPCWSTR wzFile ) { HRESULT hr = S_OK; @@ -1507,13 +1522,13 @@ extern "C" HRESULT DAPI FileEnsureDelete( { if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL)) { - ExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); + FileExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); } } if (!::DeleteFileW(wzFile)) { - ExitOnLastError(hr, "Failed to delete file: %ls", wzFile); + FileExitOnLastError(hr, "Failed to delete file: %ls", wzFile); } } @@ -1525,7 +1540,7 @@ LExit: FileGetTime - Gets the file time of a specified file ********************************************************************/ extern "C" HRESULT DAPI FileGetTime( - __in LPCWSTR wzFile, + __in_z LPCWSTR wzFile, __out_opt LPFILETIME lpCreationTime, __out_opt LPFILETIME lpLastAccessTime, __out_opt LPFILETIME lpLastWriteTime @@ -1535,11 +1550,11 @@ extern "C" HRESULT DAPI FileGetTime( HANDLE hFile = NULL; hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); - ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) { - ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); } LExit: @@ -1551,7 +1566,7 @@ LExit: FileSetTime - Sets the file time of a specified file ********************************************************************/ extern "C" HRESULT DAPI FileSetTime( - __in LPCWSTR wzFile, + __in_z LPCWSTR wzFile, __in_opt const FILETIME *lpCreationTime, __in_opt const FILETIME *lpLastAccessTime, __in_opt const FILETIME *lpLastWriteTime @@ -1561,11 +1576,11 @@ extern "C" HRESULT DAPI FileSetTime( HANDLE hFile = NULL; hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); - ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) { - ExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); + FileExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); } LExit: @@ -1578,7 +1593,7 @@ LExit: creation time of the file ********************************************************************/ extern "C" HRESULT DAPI FileResetTime( - __in LPCWSTR wzFile + __in_z LPCWSTR wzFile ) { HRESULT hr = S_OK; @@ -1586,16 +1601,16 @@ extern "C" HRESULT DAPI FileResetTime( FILETIME ftCreateTime; hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL)) { - ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); } if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime)) { - ExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); + FileExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); } LExit: @@ -1609,7 +1624,7 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI FileExecutableArchitecture( - __in LPCWSTR wzFile, + __in_z LPCWSTR wzFile, __out FILE_ARCHITECTURE *pArchitecture ) { @@ -1623,34 +1638,34 @@ extern "C" HRESULT DAPI FileExecutableArchitecture( hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) { - ExitWithLastError(hr, "Failed to open file: %ls", wzFile); + FileExitWithLastError(hr, "Failed to open file: %ls", wzFile); } if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL)) { - ExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); + FileExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); } if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) { hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - ExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); + FileExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); } if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN)) { - ExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); + FileExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); } if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL)) { - ExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); + FileExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); } if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) { hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - ExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); + FileExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); } if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem || @@ -1677,7 +1692,7 @@ extern "C" HRESULT DAPI FileExecutableArchitecture( { hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); } - ExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); + FileExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); LExit: if (hFile != INVALID_HANDLE_VALUE) @@ -1706,7 +1721,7 @@ extern "C" HRESULT DAPI FileToString( // Check if the file is ANSI hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile); - ExitOnFailure(hr, "Failed to read file: %ls", wzFile); + FileExitOnFailure(hr, "Failed to read file: %ls", wzFile); if (0 == cbFullFileBuffer) { @@ -1723,7 +1738,7 @@ extern "C" HRESULT DAPI FileToString( } hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8); - ExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); + FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); *psczString = sczFileText; sczFileText = NULL; @@ -1737,7 +1752,7 @@ extern "C" HRESULT DAPI FileToString( } hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR)); - ExitOnFailure(hr, "Failed to allocate copy of string"); + FileExitOnFailure(hr, "Failed to allocate copy of string"); } // No BOM, let's try to detect else @@ -1763,7 +1778,7 @@ extern "C" HRESULT DAPI FileToString( { if (E_OUTOFMEMORY == hr) { - ExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); + FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); } } else @@ -1780,7 +1795,7 @@ extern "C" HRESULT DAPI FileToString( } hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR)); - ExitOnFailure(hr, "Failed to allocate copy of string"); + FileExitOnFailure(hr, "Failed to allocate copy of string"); } } @@ -1813,20 +1828,20 @@ extern "C" HRESULT DAPI FileFromString( { case FILE_ENCODING_UTF8: hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); - ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); cbFullFileBuffer = lstrlenA(sczUtf8String); pcbFullFileBuffer = reinterpret_cast(sczUtf8String); break; case FILE_ENCODING_UTF8_WITH_BOM: hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); - ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); cbStrLen = lstrlenA(sczUtf8String); cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); - ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM)); memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen); @@ -1841,7 +1856,7 @@ extern "C" HRESULT DAPI FileFromString( cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); - ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM)); memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen); @@ -1850,7 +1865,7 @@ extern "C" HRESULT DAPI FileFromString( } hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL); - ExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); + FileExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); LExit: ReleaseStr(sczUtf8String); diff --git a/src/dutil/gdiputil.cpp b/src/dutil/gdiputil.cpp index aef6178f..b5a0087c 100644 --- a/src/dutil/gdiputil.cpp +++ b/src/dutil/gdiputil.cpp @@ -4,6 +4,21 @@ using namespace Gdiplus; + +// Exit macros +#define GdipExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) +#define GdipExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) +#define GdipExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) +#define GdipExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) +#define GdipExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GDIPUTIL, e, x, s, __VA_ARGS__) +#define GdipExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GDIPUTIL, g, x, s, __VA_ARGS__) + /******************************************************************** GdipInitialize - initializes GDI+. @@ -22,7 +37,7 @@ extern "C" HRESULT DAPI GdipInitialize( Status status = Ok; status = GdiplusStartup(pToken, pInput, pOutput); - ExitOnGdipFailure(status, hr, "Failed to initialize GDI+."); + GdipExitOnGdipFailure(status, hr, "Failed to initialize GDI+."); LExit: return hr; @@ -59,15 +74,15 @@ extern "C" HRESULT DAPI GdipBitmapFromResource( Status gs = Ok; hr = ResReadData(hinst, szId, &pvData, &cbData); - ExitOnFailure(hr, "Failed to load GDI+ bitmap from resource."); + GdipExitOnFailure(hr, "Failed to load GDI+ bitmap from resource."); // Have to copy the fixed resource data into moveable (heap) memory // since that's what GDI+ expects. hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData); - ExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory."); + GdipExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory."); pv = ::GlobalLock(hGlobal); - ExitOnNullWithLastError(pv, hr, "Failed to lock global memory."); + GdipExitOnNullWithLastError(pv, hr, "Failed to lock global memory."); memcpy(pv, pvData, cbData); @@ -75,15 +90,15 @@ extern "C" HRESULT DAPI GdipBitmapFromResource( pv = NULL; hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); - ExitOnFailure(hr, "Failed to allocate stream from global memory."); + GdipExitOnFailure(hr, "Failed to allocate stream from global memory."); hGlobal = NULL; // we gave the global memory to the stream object so it will close it pBitmap = Bitmap::FromStream(pStream); - ExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream."); + GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream."); gs = pBitmap->GetLastStatus(); - ExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream."); + GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream."); *ppBitmap = pBitmap; pBitmap = NULL; @@ -123,13 +138,13 @@ extern "C" HRESULT DAPI GdipBitmapFromFile( Bitmap *pBitmap = NULL; Status gs = Ok; - ExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName"); + GdipExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName"); pBitmap = Bitmap::FromFile(wzFileName); - ExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file."); + GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file."); gs = pBitmap->GetLastStatus(); - ExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName); + GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName); *ppBitmap = pBitmap; pBitmap = NULL; diff --git a/src/dutil/guidutil.cpp b/src/dutil/guidutil.cpp index c0353892..204c9af2 100644 --- a/src/dutil/guidutil.cpp +++ b/src/dutil/guidutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define GuidExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) +#define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) +#define GuidExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) +#define GuidExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) +#define GuidExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GUIDUTIL, e, x, s, __VA_ARGS__) +#define GuidExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GUIDUTIL, g, x, s, __VA_ARGS__) + extern "C" HRESULT DAPI GuidFixedCreate( _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid ) @@ -10,12 +25,12 @@ extern "C" HRESULT DAPI GuidFixedCreate( UUID guid = { }; hr = HRESULT_FROM_RPC(::UuidCreate(&guid)); - ExitOnFailure(hr, "UuidCreate failed."); + GuidExitOnFailure(hr, "UuidCreate failed."); if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) { hr = E_OUTOFMEMORY; - ExitOnRootFailure(hr, "Failed to convert guid into string."); + GuidExitOnRootFailure(hr, "Failed to convert guid into string."); } LExit: @@ -29,10 +44,10 @@ extern "C" HRESULT DAPI GuidCreate( HRESULT hr = S_OK; hr = StrAlloc(psczGuid, GUID_STRING_LENGTH); - ExitOnFailure(hr, "Failed to allocate space for guid"); + GuidExitOnFailure(hr, "Failed to allocate space for guid"); hr = GuidFixedCreate(*psczGuid); - ExitOnFailure(hr, "Failed to create new guid."); + GuidExitOnFailure(hr, "Failed to create new guid."); LExit: return hr; diff --git a/src/dutil/iis7util.cpp b/src/dutil/iis7util.cpp index 04165a8d..d0a0b000 100644 --- a/src/dutil/iis7util.cpp +++ b/src/dutil/iis7util.cpp @@ -3,6 +3,21 @@ #include "precomp.h" #include "iis7util.h" + +// Exit macros +#define IisExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) +#define IisExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) +#define IisExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) +#define IisExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) +#define IisExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_IIS7UTIL, e, x, s, __VA_ARGS__) +#define IisExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_IIS7UTIL, g, x, s, __VA_ARGS__) + #define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt) extern "C" HRESULT DAPI Iis7PutPropertyVariant( @@ -16,13 +31,13 @@ extern "C" HRESULT DAPI Iis7PutPropertyVariant( BSTR bstrPropName = NULL; bstrPropName = ::SysAllocString(wzPropName); - ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); hr = pElement->GetPropertyByName(bstrPropName, &pProperty); - ExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); hr = pProperty->put_Value(vtPut); - ExitOnFailure(hr, "Failed to set property value for %ls", wzPropName); + IisExitOnFailure(hr, "Failed to set property value for %ls", wzPropName); LExit: ReleaseBSTR(bstrPropName); @@ -44,7 +59,7 @@ extern "C" HRESULT DAPI Iis7PutPropertyString( ::VariantInit(&vtPut); vtPut.vt = VT_BSTR; vtPut.bstrVal = ::SysAllocString(wzString); - ExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + IisExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut); @@ -92,13 +107,13 @@ extern "C" HRESULT DAPI Iis7GetPropertyVariant( BSTR bstrPropName = NULL; bstrPropName = ::SysAllocString(wzPropName); - ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); hr = pElement->GetPropertyByName(bstrPropName, &pProperty); - ExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); hr = pProperty->get_Value(vtGet); - ExitOnFailure(hr, "Failed to get property value for %ls", wzPropName); + IisExitOnFailure(hr, "Failed to get property value for %ls", wzPropName); LExit: ReleaseBSTR(bstrPropName); @@ -119,12 +134,12 @@ extern "C" HRESULT DAPI Iis7GetPropertyString( ::VariantInit(&vtGet); hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet); - ExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName); + IisExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName); if (!ISSTRINGVARIANT(vtGet.vt)) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt); + IisExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt); } hr = StrAllocString(psczGet, vtGet.bstrVal, 0); @@ -198,13 +213,13 @@ BOOL DAPI CompareVariantPath( if (ISSTRINGVARIANT(pVariant1->vt)) { hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - ExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal); + IisExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal); } if (ISSTRINGVARIANT(pVariant2->vt)) { hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - ExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal); + IisExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal); } fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1); @@ -242,14 +257,14 @@ extern "C" BOOL DAPI Iis7IsMatchingAppHostElement( VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault; hr = pElement->get_Name(&bstrElementName); - ExitOnFailure(hr, "Failed to get name of element"); + IisExitOnFailure(hr, "Failed to get name of element"); if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1)) { ExitFunction(); } hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue); - ExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName); + IisExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName); if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue)) { @@ -274,7 +289,9 @@ BOOL DAPI IsMatchingAppHostMethod( BSTR bstrName = NULL; hr = pMethod->get_Name(&bstrName); - ExitOnFailure(hr, "Failed to get name of element"); + IisExitOnFailure(hr, "Failed to get name of element"); + + Assert(bstrName); if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1)) { @@ -303,7 +320,7 @@ extern "C" HRESULT DAPI Iis7FindAppHostElementPath( vtValue.vt = VT_BSTR; vtValue.bstrVal = ::SysAllocString(wzAttributeValue); - ExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); comparison.sczElementName = wzElementName; comparison.sczAttributeName = wzAttributeName; @@ -337,7 +354,7 @@ extern "C" HRESULT DAPI Iis7FindAppHostElementString( vtValue.vt = VT_BSTR; vtValue.bstrVal = ::SysAllocString(wzAttributeValue); - ExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); hr = Iis7FindAppHostElementVariant(pCollection, wzElementName, @@ -427,14 +444,14 @@ extern "C" HRESULT DAPI Iis7EnumAppHostElements( } hr = pCollection->get_Count(&dwElements); - ExitOnFailure(hr, "Failed get application IAppHostElementCollection count"); + IisExitOnFailure(hr, "Failed get application IAppHostElementCollection count"); vtIndex.vt = VT_UI4; for (DWORD i = 0; i < dwElements; ++i) { vtIndex.ulVal = i; hr = pCollection->get_Item(vtIndex , &pElement); - ExitOnFailure(hr, "Failed get IAppHostElement element"); + IisExitOnFailure(hr, "Failed get IAppHostElement element"); if (pCallback(pElement, pContext)) { @@ -484,14 +501,14 @@ extern "C" HRESULT DAPI Iis7FindAppHostMethod( } hr = pCollection->get_Count(&dwMethods); - ExitOnFailure(hr, "Failed get application IAppHostMethodCollection count"); + IisExitOnFailure(hr, "Failed get application IAppHostMethodCollection count"); vtIndex.vt = VT_UI4; for (DWORD i = 0; i < dwMethods; ++i) { vtIndex.ulVal = i; hr = pCollection->get_Item(vtIndex , &pMethod); - ExitOnFailure(hr, "Failed get IAppHostMethod element"); + IisExitOnFailure(hr, "Failed get IAppHostMethod element"); if (IsMatchingAppHostMethod(pMethod, wzMethodName)) { diff --git a/src/dutil/inc/atomutil.h b/src/dutil/inc/atomutil.h index ff869c4a..9acfc1d5 100644 --- a/src/dutil/inc/atomutil.h +++ b/src/dutil/inc/atomutil.h @@ -138,7 +138,7 @@ HRESULT DAPI AtomParseFromDocument( ); void DAPI AtomFreeFeed( - __in_xcount(pFeed->cItems) ATOM_FEED *pFEED + __in_xcount(pFeed->cItems) ATOM_FEED* pFeed ); #ifdef __cplusplus diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h index a718e9c0..7509f76a 100644 --- a/src/dutil/inc/buffutil.h +++ b/src/dutil/inc/buffutil.h @@ -50,37 +50,37 @@ HRESULT BuffReadStream( __in_bcount(cbBuffer) const BYTE* pbBuffer, __in SIZE_T cbBuffer, __inout SIZE_T* piBuffer, - __deref_out_bcount(*pcbStream) BYTE** ppbStream, + __deref_inout_bcount(*pcbStream) BYTE** ppbStream, __out SIZE_T* pcbStream ); HRESULT BuffWriteNumber( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in DWORD_PTR dw ); HRESULT BuffWriteNumber64( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in DWORD64 dw64 ); HRESULT BuffWritePointer( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in DWORD_PTR dw ); HRESULT BuffWriteString( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in_z_opt LPCWSTR scz ); HRESULT BuffWriteStringAnsi( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in_z_opt LPCSTR scz ); HRESULT BuffWriteStream( - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in_bcount(cbStream) const BYTE* pbStream, __in SIZE_T cbStream diff --git a/src/dutil/inc/conutil.h b/src/dutil/inc/conutil.h index 5f611d01..38aaea84 100644 --- a/src/dutil/inc/conutil.h +++ b/src/dutil/inc/conutil.h @@ -55,12 +55,12 @@ HRESULT DAPI ConsoleReadW( ); HRESULT DAPI ConsoleReadStringA( - __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, CONST DWORD cchCharBuffer, __out DWORD* pcchNumCharReturn ); HRESULT DAPI ConsoleReadStringW( - __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, CONST DWORD cchCharBuffer, __out DWORD* pcchNumCharReturn ); diff --git a/src/dutil/inc/deputil.h b/src/dutil/inc/deputil.h index 8f5f0ae8..bfe235f3 100644 --- a/src/dutil/inc/deputil.h +++ b/src/dutil/inc/deputil.h @@ -55,7 +55,7 @@ DAPI_(HRESULT) DepCheckDependency( DAPI_(HRESULT) DepCheckDependents( __in HKEY hkHive, __in_z LPCWSTR wzProviderKey, - __in int iAttributes, + __reserved int iAttributes, __in C_STRINGDICT_HANDLE sdIgnoredDependents, __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, __inout LPUINT pcDependents diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h index 15d45d21..fc9ec0f4 100644 --- a/src/dutil/inc/dutil.h +++ b/src/dutil/inc/dutil.h @@ -44,7 +44,7 @@ void DAPI DutilUninitialize(); void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule); void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn); void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine); -void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z LPCSTR szMessage); +void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z __format_string LPCSTR szMessage); void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames); REPORT_LEVEL DAPI Dutil_TraceGetLevel(); diff --git a/src/dutil/inc/eseutil.h b/src/dutil/inc/eseutil.h index 1c408927..bea47b2b 100644 --- a/src/dutil/inc/eseutil.h +++ b/src/dutil/inc/eseutil.h @@ -160,7 +160,7 @@ HRESULT DAPI EseGetColumnBinary( __in JET_SESID jsSession, __in ESE_TABLE_SCHEMA tsTable, __in DWORD dwColumn, - __deref_out_bcount(*piBuffer) BYTE** ppbBuffer, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer ); HRESULT DAPI EseGetColumnDword( diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h index d2b2f4fe..7caa62b8 100644 --- a/src/dutil/inc/fileutil.h +++ b/src/dutil/inc/fileutil.h @@ -121,7 +121,7 @@ HRESULT DAPI FileReadPartial( __in BOOL fPartialOK ); HRESULT DAPI FileReadPartialEx( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, __out_range(<=, cbMaxRead) SIZE_T* pcbDest, __in_z LPCWSTR wzSrcPath, __in BOOL fSeek, diff --git a/src/dutil/inc/inetutil.h b/src/dutil/inc/inetutil.h index 4cbf510b..19ace88b 100644 --- a/src/dutil/inc/inetutil.h +++ b/src/dutil/inc/inetutil.h @@ -30,7 +30,7 @@ HRESULT DAPI InternetQueryInfoString( HRESULT DAPI InternetQueryInfoNumber( __in HINTERNET h, __in DWORD dwInfo, - __out LONG* plInfo + __inout LONG* plInfo ); #ifdef __cplusplus diff --git a/src/dutil/inc/iniutil.h b/src/dutil/inc/iniutil.h index d5b50c17..c8503155 100644 --- a/src/dutil/inc/iniutil.h +++ b/src/dutil/inc/iniutil.h @@ -55,7 +55,7 @@ HRESULT DAPI IniParse( // (their value will be NULL) HRESULT DAPI IniGetValueList( __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __deref_out_ecount_opt(pcValues) INI_VALUE** prgivValues, + __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, __out DWORD *pcValues ); HRESULT DAPI IniGetValue( diff --git a/src/dutil/inc/memutil.h b/src/dutil/inc/memutil.h index 93e53228..49f86e0a 100644 --- a/src/dutil/inc/memutil.h +++ b/src/dutil/inc/memutil.h @@ -39,13 +39,13 @@ HRESULT DAPI MemReAllocArray( __in DWORD dwNewItemCount ); HRESULT DAPI MemEnsureArraySize( - __deref_out_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, __in DWORD cArray, __in SIZE_T cbArrayType, __in DWORD dwGrowthCount ); HRESULT DAPI MemInsertIntoArray( - __deref_out_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, __in DWORD dwInsertIndex, __in DWORD cInsertItems, __in DWORD cExistingArray, @@ -61,7 +61,7 @@ void DAPI MemRemoveFromArray( __in BOOL fPreserveOrder ); void DAPI MemArraySwapItems( - __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __inout_bcount(cbArrayType) LPVOID pvArray, __in DWORD dwIndex1, __in DWORD dwIndex2, __in SIZE_T cbArrayType diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h index bee8ed1b..f4f4e59c 100644 --- a/src/dutil/inc/pathutil.h +++ b/src/dutil/inc/pathutil.h @@ -19,7 +19,7 @@ typedef enum PATH_EXPAND (i.e. quote arguments with spaces in them). ********************************************************************/ DAPI_(HRESULT) PathCommandLineAppend( - __deref_out_z LPWSTR* psczCommandLine, + __deref_inout_z LPWSTR* psczCommandLine, __in_z LPCWSTR wzArgument ); @@ -43,7 +43,7 @@ DAPI_(LPCWSTR) PathExtension( ********************************************************************/ DAPI_(HRESULT) PathGetDirectory( __in_z LPCWSTR wzPath, - __out LPWSTR *psczDirectory + __out_z LPWSTR *psczDirectory ); /******************************************************************* @@ -206,7 +206,7 @@ DAPI_(HRESULT) PathCompress( *******************************************************************/ DAPI_(HRESULT) PathGetHierarchyArray( __in_z LPCWSTR wzPath, - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczPathArray, + __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, __inout LPUINT pcPathArray ); diff --git a/src/dutil/inc/regutil.h b/src/dutil/inc/regutil.h index 897b9d03..2f09d244 100644 --- a/src/dutil/inc/regutil.h +++ b/src/dutil/inc/regutil.h @@ -50,7 +50,7 @@ typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)( __out LPWSTR lpName, __inout LPDWORD lpcName, __reserved LPDWORD lpReserved, - __inout LPWSTR lpClass, + __inout_opt LPWSTR lpClass, __inout_opt LPDWORD lpcClass, __out_opt PFILETIME lpftLastWriteTime ); @@ -66,7 +66,7 @@ typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)( ); typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)( __in HKEY hKey, - __out LPWSTR lpClass, + __out_opt LPWSTR lpClass, __inout_opt LPDWORD lpcClass, __reserved LPDWORD lpReserved, __out_opt LPDWORD lpcSubKeys, @@ -170,7 +170,7 @@ HRESULT DAPI RegReadString( HRESULT DAPI RegReadStringArray( __in HKEY hk, __in_z_opt LPCWSTR wzName, - __deref_out_ecount_opt(pcStrings) LPWSTR** prgsczStrings, + __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, __out DWORD *pcStrings ); HRESULT DAPI RegReadVersion( @@ -202,7 +202,7 @@ HRESULT DAPI RegWriteString( HRESULT DAPI RegWriteStringArray( __in HKEY hk, __in_z_opt LPCWSTR wzName, - __in_ecount(cValues) LPWSTR *rgwzStrings, + __in_ecount(cStrings) LPWSTR *rgwzStrings, __in DWORD cStrings ); HRESULT DAPI RegWriteStringFormatted( diff --git a/src/dutil/inc/shelutil.h b/src/dutil/inc/shelutil.h index 21e82672..0b9f539d 100644 --- a/src/dutil/inc/shelutil.h +++ b/src/dutil/inc/shelutil.h @@ -19,9 +19,9 @@ void DAPI ShelFunctionOverride( ); HRESULT DAPI ShelExec( __in_z LPCWSTR wzTargetPath, - __in_opt LPCWSTR wzParameters, - __in_opt LPCWSTR wzVerb, - __in_opt LPCWSTR wzWorkingDirectory, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, __in int nShowCmd, __in_opt HWND hwndParent, __out_opt HANDLE* phProcess diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h index 187bfda8..cf8c751c 100644 --- a/src/dutil/inc/strutil.h +++ b/src/dutil/inc/strutil.h @@ -198,7 +198,7 @@ HRESULT DAPI StrAllocBase85Decode( HRESULT DAPI MultiSzLen( __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, - __out SIZE_T* pcbch + __out SIZE_T* pcch ); HRESULT DAPI MultiSzPrepend( __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, @@ -222,7 +222,7 @@ HRESULT DAPI MultiSzRemoveString( __in DWORD_PTR dwIndex ); HRESULT DAPI MultiSzInsertString( - __deref_inout_z LPWSTR* ppwzMultiSz, + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, __inout_opt SIZE_T* pcchMultiSz, __in DWORD_PTR dwIndex, __in_z LPCWSTR pwzInsert diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index 11eac9c2..d3dd6d21 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -737,7 +737,7 @@ HRESULT DAPI ThemeSetTextControlEx( HRESULT DAPI ThemeGetTextControl( __in const THEME* pTheme, __in DWORD dwControl, - __out_z LPWSTR* psczText + __inout_z LPWSTR* psczText ); /******************************************************************** diff --git a/src/dutil/inc/uriutil.h b/src/dutil/inc/uriutil.h index 52e78308..d6dfdd6b 100644 --- a/src/dutil/inc/uriutil.h +++ b/src/dutil/inc/uriutil.h @@ -91,7 +91,7 @@ HRESULT DAPI UriResolve( __in_z LPCWSTR wzUri, __in_opt LPCWSTR wzBaseUri, __out LPWSTR* ppwzResolvedUri, - __out_opt const URI_PROTOCOL* pResolvedProtocol + __out_opt URI_PROTOCOL* pResolvedProtocol ); #ifdef __cplusplus diff --git a/src/dutil/inc/wiutil.h b/src/dutil/inc/wiutil.h index 10d003b0..9c2de209 100644 --- a/src/dutil/inc/wiutil.h +++ b/src/dutil/inc/wiutil.h @@ -330,7 +330,7 @@ HRESULT DAPI WiuEnumRelatedProducts( ); HRESULT DAPI WiuEnumRelatedProductCodes( __in_z LPCWSTR wzUpgradeCode, - __deref_out_ecount_opt(pcRelatedProducts) LPWSTR** prgsczProductCodes, + __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, __out DWORD* pcRelatedProducts, __in BOOL fReturnHighestVersionOnly ); diff --git a/src/dutil/inetutil.cpp b/src/dutil/inetutil.cpp index 69a0176a..f75849f6 100644 --- a/src/dutil/inetutil.cpp +++ b/src/dutil/inetutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define InetExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) +#define InetExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) +#define InetExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) +#define InetExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) +#define InetExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INETUTIL, e, x, s, __VA_ARGS__) +#define InetExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INETUTIL, g, x, s, __VA_ARGS__) + + /******************************************************************* InternetGetSizeByHandle - returns size of file by url handle @@ -15,13 +30,13 @@ extern "C" HRESULT DAPI InternetGetSizeByHandle( Assert(pllSize); HRESULT hr = S_OK; - DWORD dwSize; - DWORD cb; + DWORD dwSize = 0; + DWORD cb = 0; cb = sizeof(dwSize); if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast(&dwSize), &cb, NULL)) { - ExitOnLastError(hr, "Failed to get size for internet file handle"); + InetExitOnLastError(hr, "Failed to get size for internet file handle"); } *pllSize = dwSize; @@ -47,12 +62,12 @@ extern "C" HRESULT DAPI InternetGetCreateTimeByHandle( if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast(&st), &cb, NULL)) { - ExitWithLastError(hr, "failed to get create time for internet file handle"); + InetExitWithLastError(hr, "failed to get create time for internet file handle"); } if (!::SystemTimeToFileTime(&st, pft)) { - ExitWithLastError(hr, "failed to convert system time to file time"); + InetExitWithLastError(hr, "failed to convert system time to file time"); } LExit: @@ -78,11 +93,11 @@ extern "C" HRESULT DAPI InternetQueryInfoString( if (!*psczValue) { hr = StrAlloc(psczValue, 64); - ExitOnFailure(hr, "Failed to allocate memory for value."); + InetExitOnFailure(hr, "Failed to allocate memory for value."); } hr = StrSize(*psczValue, &cbValue); - ExitOnFailure(hr, "Failed to get size of value."); + InetExitOnFailure(hr, "Failed to get size of value."); if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), reinterpret_cast(&cbValue), &dwIndex)) { @@ -92,7 +107,7 @@ extern "C" HRESULT DAPI InternetQueryInfoString( cbValue += sizeof(WCHAR); // add one character for the null terminator. hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR)); - ExitOnFailure(hr, "Failed to allocate value."); + InetExitOnFailure(hr, "Failed to allocate value."); if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), reinterpret_cast(&cbValue), &dwIndex)) { @@ -105,7 +120,7 @@ extern "C" HRESULT DAPI InternetQueryInfoString( } hr = HRESULT_FROM_WIN32(er); - ExitOnRootFailure(hr, "Failed to get query information."); + InetExitOnRootFailure(hr, "Failed to get query information."); } LExit: @@ -120,7 +135,7 @@ LExit: extern "C" HRESULT DAPI InternetQueryInfoNumber( __in HINTERNET hRequest, __in DWORD dwInfo, - __out LONG* plInfo + __inout LONG* plInfo ) { HRESULT hr = S_OK; @@ -129,7 +144,7 @@ extern "C" HRESULT DAPI InternetQueryInfoNumber( if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast(plInfo), &cbCode, &dwIndex)) { - ExitWithLastError(hr, "Failed to get query information."); + InetExitWithLastError(hr, "Failed to get query information."); } LExit: diff --git a/src/dutil/iniutil.cpp b/src/dutil/iniutil.cpp index c9ef6c3d..70b62995 100644 --- a/src/dutil/iniutil.cpp +++ b/src/dutil/iniutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__) +#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__) + const LPCWSTR wzSectionSeparator = L"\\"; struct INI_STRUCT @@ -33,7 +48,7 @@ const int INI_HANDLE_BYTES = sizeof(INI_STRUCT); static HRESULT GetSectionPrefixFromName( __in_z LPCWSTR wzName, - __deref_out_z LPWSTR* psczOutput + __deref_inout_z LPWSTR* psczOutput ); static void UninitializeIniValue( INI_VALUE *pivValue @@ -47,7 +62,7 @@ extern "C" HRESULT DAPI IniInitialize( // Allocate the handle *piHandle = static_cast(MemAlloc(sizeof(INI_STRUCT), TRUE)); - ExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); + IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); LExit: return hr; @@ -96,7 +111,7 @@ extern "C" HRESULT DAPI IniSetOpenTag( if (wzOpenTagPrefix) { hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0); - ExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); + IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); } else { @@ -106,7 +121,7 @@ extern "C" HRESULT DAPI IniSetOpenTag( if (wzOpenTagPostfix) { hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0); - ExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); + IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); } else { @@ -130,7 +145,7 @@ extern "C" HRESULT DAPI IniSetValueStyle( if (wzValuePrefix) { hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0); - ExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); + IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); } else { @@ -140,7 +155,7 @@ extern "C" HRESULT DAPI IniSetValueStyle( if (wzValueSeparator) { hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0); - ExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); + IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); } else { @@ -162,12 +177,12 @@ extern "C" HRESULT DAPI IniSetValueSeparatorException( INI_STRUCT *pi = static_cast(piHandle); hr = MemEnsureArraySize(reinterpret_cast(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5); - ExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); + IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); dwInsertedIndex = pi->cValueSeparatorExceptions; ++pi->cValueSeparatorExceptions; hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0); - ExitOnFailure(hr, "Failed to copy value separator exception"); + IniExitOnFailure(hr, "Failed to copy value separator exception"); LExit: return hr; @@ -185,7 +200,7 @@ extern "C" HRESULT DAPI IniSetCommentStyle( if (wzLinePrefix) { hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0); - ExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); + IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); } else { @@ -226,10 +241,10 @@ extern "C" HRESULT DAPI IniParse( BOOL fValuePrefix = (NULL != pi->sczValuePrefix); hr = StrAllocString(&pi->sczPath, wzPath, 0); - ExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); + IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding); - ExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); + IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); if (pfeEncodingFound) { @@ -244,7 +259,7 @@ extern "C" HRESULT DAPI IniParse( dwValuePrefixLength = lstrlenW(pi->sczValuePrefix); hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast(&pi->cLines), sczContents, L"\n"); - ExitOnFailure(hr, "Failed to split INI file into lines"); + IniExitOnFailure(hr, "Failed to split INI file into lines"); for (DWORD i = 0; i < pi->cLines; ++i) { @@ -324,7 +339,7 @@ extern "C" HRESULT DAPI IniParse( { // There is an section starting here, let's keep track of it and move on hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix))); - ExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); + IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output ReleaseNullStr(pi->rgsczLines[i]); @@ -342,28 +357,28 @@ extern "C" HRESULT DAPI IniParse( } hr = MemEnsureArraySize(reinterpret_cast(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100); - ExitOnFailure(hr, "Failed to increase array size for value array"); + IniExitOnFailure(hr, "Failed to increase array size for value array"); if (sczCurrentSection) { hr = StrAllocString(&sczName, sczCurrentSection, 0); - ExitOnFailure(hr, "Failed to copy current section name"); + IniExitOnFailure(hr, "Failed to copy current section name"); hr = StrAllocConcat(&sczName, wzSectionSeparator, 0); - ExitOnFailure(hr, "Failed to copy current section name"); + IniExitOnFailure(hr, "Failed to copy current section name"); } hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin); - ExitOnFailure(hr, "Failed to copy name"); + IniExitOnFailure(hr, "Failed to copy name"); hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0); - ExitOnFailure(hr, "Failed to copy value"); + IniExitOnFailure(hr, "Failed to copy value"); hr = StrTrimWhitespace(&sczNameTrimmed, sczName); - ExitOnFailure(hr, "Failed to trim whitespace from name"); + IniExitOnFailure(hr, "Failed to trim whitespace from name"); hr = StrTrimWhitespace(&sczValueTrimmed, sczValue); - ExitOnFailure(hr, "Failed to trim whitespace from value"); + IniExitOnFailure(hr, "Failed to trim whitespace from value"); pi->rgivValues[pi->cValues].wzName = const_cast(sczNameTrimmed); sczNameTrimmed = NULL; @@ -397,7 +412,7 @@ LExit: extern "C" HRESULT DAPI IniGetValueList( __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __deref_out_ecount_opt(pcValues) INI_VALUE** prgivValues, + __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, __out DWORD *pcValues ) { @@ -434,7 +449,7 @@ extern "C" HRESULT DAPI IniGetValue( if (NULL == pValue) { hr = E_NOTFOUND; - ExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); + IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); } if (NULL == pValue->wzValue) @@ -443,7 +458,7 @@ extern "C" HRESULT DAPI IniGetValue( } hr = StrAllocString(psczValue, pValue->wzValue, 0); - ExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); + IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); LExit: return hr; @@ -494,7 +509,7 @@ extern "C" HRESULT DAPI IniSetValue( { pi->fModified = TRUE; hr = StrAllocString(const_cast(&pValue->wzValue), wzValue, 0); - ExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); + IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); } ExitFunction1(hr = S_OK); @@ -504,7 +519,7 @@ extern "C" HRESULT DAPI IniSetValue( if (wzValueName) { hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix); - ExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); + IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); } // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in) @@ -545,13 +560,13 @@ extern "C" HRESULT DAPI IniSetValue( pi->fModified = TRUE; hr = MemInsertIntoArray(reinterpret_cast(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100); - ExitOnFailure(hr, "Failed to insert value into array"); + IniExitOnFailure(hr, "Failed to insert value into array"); hr = StrAllocString(&sczName, wzValueName, 0); - ExitOnFailure(hr, "Failed to copy name"); + IniExitOnFailure(hr, "Failed to copy name"); hr = StrAllocString(&sczValue, wzValue, 0); - ExitOnFailure(hr, "Failed to copy value"); + IniExitOnFailure(hr, "Failed to copy value"); pi->rgivValues[dwInsertIndex].wzName = const_cast(sczName); sczName = NULL; @@ -611,7 +626,7 @@ extern "C" HRESULT DAPI IniWriteFile( BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix); hr = StrAllocString(&sczContents, L"", 0); - ExitOnFailure(hr, "Failed to begin contents string as empty string"); + IniExitOnFailure(hr, "Failed to begin contents string as empty string"); // Insert any beginning lines we didn't understand like comments if (0 < pi->cLines) @@ -619,10 +634,10 @@ extern "C" HRESULT DAPI IniWriteFile( while (pi->rgsczLines[dwLineArrayIndex]) { hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0); - ExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); hr = StrAllocConcat(&sczContents, L"\r\n", 2); - ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); ++dwLineArrayIndex; } @@ -640,23 +655,23 @@ extern "C" HRESULT DAPI IniWriteFile( // First see if we need to write a section line hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix); - ExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); + IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); // If the new section prefix is different, write a section out for it if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1))) { hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0); - ExitOnFailure(hr, "Failed to concat open tag prefix to string"); + IniExitOnFailure(hr, "Failed to concat open tag prefix to string"); // Exclude section separator (i.e. backslash) from new section prefix hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator)); - ExitOnFailure(hr, "Failed to concat section name to string"); + IniExitOnFailure(hr, "Failed to concat section name to string"); hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0); - ExitOnFailure(hr, "Failed to concat open tag postfix to string"); + IniExitOnFailure(hr, "Failed to concat open tag postfix to string"); hr = StrAllocConcat(&sczContents, L"\r\n", 2); - ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); ReleaseNullStr(sczCurrentSectionPrefix); sczCurrentSectionPrefix = sczNewSectionPrefix; @@ -674,10 +689,10 @@ extern "C" HRESULT DAPI IniWriteFile( } hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0); - ExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); hr = StrAllocConcat(&sczContents, L"\r\n", 2); - ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); } wzName = pi->rgivValues[i].wzName; @@ -690,20 +705,20 @@ extern "C" HRESULT DAPI IniWriteFile( if (pi->sczValuePrefix) { hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0); - ExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); + IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); } hr = StrAllocConcat(&sczContents, wzName, 0); - ExitOnFailure(hr, "Failed to concat value name to ini output buffer"); + IniExitOnFailure(hr, "Failed to concat value name to ini output buffer"); hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0); - ExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); + IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0); - ExitOnFailure(hr, "Failed to concat value to ini output buffer"); + IniExitOnFailure(hr, "Failed to concat value to ini output buffer"); hr = StrAllocConcat(&sczContents, L"\r\n", 2); - ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); } // If no path was specified, use the path to the file we parsed @@ -713,7 +728,7 @@ extern "C" HRESULT DAPI IniWriteFile( } hr = FileFromString(wzPath, 0, sczContents, feEncoding); - ExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); + IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); LExit: ReleaseStr(sczContents); @@ -733,7 +748,7 @@ static void UninitializeIniValue( static HRESULT GetSectionPrefixFromName( __in_z LPCWSTR wzName, - __deref_out_z LPWSTR* psczOutput + __deref_inout_z LPWSTR* psczOutput ) { HRESULT hr = S_OK; @@ -745,7 +760,7 @@ static HRESULT GetSectionPrefixFromName( if (wzSectionDelimiter && wzSectionDelimiter != wzName) { hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1); - ExitOnFailure(hr, "Failed to copy section prefix"); + IniExitOnFailure(hr, "Failed to copy section prefix"); } LExit: diff --git a/src/dutil/jsonutil.cpp b/src/dutil/jsonutil.cpp index ba088705..3450ba59 100644 --- a/src/dutil/jsonutil.cpp +++ b/src/dutil/jsonutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define JsonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) +#define JsonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) +#define JsonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) +#define JsonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) +#define JsonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_JSONUTIL, e, x, s, __VA_ARGS__) +#define JsonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_JSONUTIL, g, x, s, __VA_ARGS__) + const DWORD JSON_STACK_INCREMENT = 5; // Prototypes @@ -44,7 +59,7 @@ DAPI_(HRESULT) JsonInitializeReader( ::InitializeCriticalSection(&pReader->cs); hr = StrAllocString(&pReader->sczJson, wzJson, 0); - ExitOnFailure(hr, "Failed to allocate json string."); + JsonExitOnFailure(hr, "Failed to allocate json string."); pReader->pwz = pReader->sczJson; @@ -153,7 +168,7 @@ DAPI_(HRESULT) JsonReadNext( { ExitFunction(); } - ExitOnFailure(hr, "Failed to get next token."); + JsonExitOnFailure(hr, "Failed to get next token."); if (JSON_TOKEN_VALUE == *pToken) { @@ -214,10 +229,10 @@ DAPI_(HRESULT) JsonWriteBool( LPWSTR sczValue = NULL; hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0); - ExitOnFailure(hr, "Failed to convert boolean to string."); + JsonExitOnFailure(hr, "Failed to convert boolean to string."); hr = DoValue(pWriter, sczValue); - ExitOnFailure(hr, "Failed to add boolean to JSON."); + JsonExitOnFailure(hr, "Failed to add boolean to JSON."); LExit: ReleaseStr(sczValue); @@ -234,10 +249,10 @@ DAPI_(HRESULT) JsonWriteNumber( LPWSTR sczValue = NULL; hr = StrAllocFormatted(&sczValue, L"%u", dwValue); - ExitOnFailure(hr, "Failed to convert number to string."); + JsonExitOnFailure(hr, "Failed to convert number to string."); hr = DoValue(pWriter, sczValue); - ExitOnFailure(hr, "Failed to add number to JSON."); + JsonExitOnFailure(hr, "Failed to add number to JSON."); LExit: ReleaseStr(sczValue); @@ -254,10 +269,10 @@ DAPI_(HRESULT) JsonWriteString( LPWSTR sczJsonString = NULL; hr = SerializeJsonString(&sczJsonString, wzValue); - ExitOnFailure(hr, "Failed to allocate string JSON."); + JsonExitOnFailure(hr, "Failed to allocate string JSON."); hr = DoValue(pWriter, sczJsonString); - ExitOnFailure(hr, "Failed to add string to JSON."); + JsonExitOnFailure(hr, "Failed to add string to JSON."); LExit: ReleaseStr(sczJsonString); @@ -272,7 +287,7 @@ DAPI_(HRESULT) JsonWriteArrayStart( HRESULT hr = S_OK; hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"["); - ExitOnFailure(hr, "Failed to start JSON array."); + JsonExitOnFailure(hr, "Failed to start JSON array."); LExit: return hr; @@ -286,7 +301,7 @@ DAPI_(HRESULT) JsonWriteArrayEnd( HRESULT hr = S_OK; hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]"); - ExitOnFailure(hr, "Failed to end JSON array."); + JsonExitOnFailure(hr, "Failed to end JSON array."); LExit: return hr; @@ -300,7 +315,7 @@ DAPI_(HRESULT) JsonWriteObjectStart( HRESULT hr = S_OK; hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{"); - ExitOnFailure(hr, "Failed to start JSON object."); + JsonExitOnFailure(hr, "Failed to start JSON object."); LExit: return hr; @@ -316,10 +331,10 @@ DAPI_(HRESULT) JsonWriteObjectKey( LPWSTR sczObjectKey = NULL; hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey); - ExitOnFailure(hr, "Failed to allocate JSON object key."); + JsonExitOnFailure(hr, "Failed to allocate JSON object key."); hr = DoKey(pWriter, sczObjectKey); - ExitOnFailure(hr, "Failed to add object key to JSON."); + JsonExitOnFailure(hr, "Failed to add object key to JSON."); LExit: ReleaseStr(sczObjectKey); @@ -334,7 +349,7 @@ DAPI_(HRESULT) JsonWriteObjectEnd( HRESULT hr = S_OK; hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}"); - ExitOnFailure(hr, "Failed to end JSON object."); + JsonExitOnFailure(hr, "Failed to end JSON object."); LExit: return hr; @@ -357,7 +372,7 @@ static HRESULT DoStart( ::EnterCriticalSection(&pWriter->cs); hr = EnsureTokenStack(pWriter); - ExitOnFailure(hr, "Failed to ensure token stack for start."); + JsonExitOnFailure(hr, "Failed to ensure token stack for start."); token = pWriter->rgTokenStack[pWriter->cTokens - 1]; switch (token) @@ -381,16 +396,16 @@ static HRESULT DoStart( hr = E_UNEXPECTED; break; } - ExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); + JsonExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); if (fNeedComma) { hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - ExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); + JsonExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); } hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); - ExitOnFailure(hr, "Failed to start JSON array or object."); + JsonExitOnFailure(hr, "Failed to start JSON array or object."); pWriter->rgTokenStack[pWriter->cTokens - 1] = token; if (fPushToken) @@ -418,7 +433,7 @@ static HRESULT DoEnd( if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) { hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); + JsonExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); } else { @@ -427,12 +442,12 @@ static HRESULT DoEnd( (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) { hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); + JsonExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); } } hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); - ExitOnFailure(hr, "Failed to end JSON array or object."); + JsonExitOnFailure(hr, "Failed to end JSON array or object."); --pWriter->cTokens; @@ -454,7 +469,7 @@ static HRESULT DoKey( ::EnterCriticalSection(&pWriter->cs); hr = EnsureTokenStack(pWriter); - ExitOnFailure(hr, "Failed to ensure token stack for key."); + JsonExitOnFailure(hr, "Failed to ensure token stack for key."); token = pWriter->rgTokenStack[pWriter->cTokens - 1]; switch (token) @@ -472,16 +487,16 @@ static HRESULT DoKey( hr = E_UNEXPECTED; break; } - ExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); + JsonExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); if (fNeedComma) { hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - ExitOnFailure(hr, "Failed to add comma for key to JSON."); + JsonExitOnFailure(hr, "Failed to add comma for key to JSON."); } hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); - ExitOnFailure(hr, "Failed to add key to JSON."); + JsonExitOnFailure(hr, "Failed to add key to JSON."); pWriter->rgTokenStack[pWriter->cTokens - 1] = token; @@ -503,7 +518,7 @@ static HRESULT DoValue( ::EnterCriticalSection(&pWriter->cs); hr = EnsureTokenStack(pWriter); - ExitOnFailure(hr, "Failed to ensure token stack for value."); + JsonExitOnFailure(hr, "Failed to ensure token stack for value."); token = pWriter->rgTokenStack[pWriter->cTokens - 1]; switch (token) @@ -528,23 +543,23 @@ static HRESULT DoValue( hr = E_UNEXPECTED; break; } - ExitOnRootFailure(hr, "Cannot add value to JSON serializer now."); + JsonExitOnRootFailure(hr, "Cannot add value to JSON serializer now."); if (fNeedComma) { hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - ExitOnFailure(hr, "Failed to add comma for value to JSON."); + JsonExitOnFailure(hr, "Failed to add comma for value to JSON."); } if (wzValue) { hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0); - ExitOnFailure(hr, "Failed to add value to JSON."); + JsonExitOnFailure(hr, "Failed to add value to JSON."); } else { hr = StrAllocConcat(&pWriter->sczJson, L"null", 0); - ExitOnFailure(hr, "Failed to add null value to JSON."); + JsonExitOnFailure(hr, "Failed to add null value to JSON."); } pWriter->rgTokenStack[pWriter->cTokens - 1] = token; @@ -563,7 +578,7 @@ static HRESULT EnsureTokenStack( DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0; hr = MemEnsureArraySize(reinterpret_cast(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT); - ExitOnFailure(hr, "Failed to allocate JSON token stack."); + JsonExitOnFailure(hr, "Failed to allocate JSON token stack."); if (0 == pWriter->cTokens) { @@ -596,7 +611,7 @@ static HRESULT SerializeJsonString( } hr = StrAlloc(psczJsonString, cchRequired); - ExitOnFailure(hr, "Failed to allocate space for JSON string."); + JsonExitOnFailure(hr, "Failed to allocate space for JSON string."); LPWSTR pchTarget = *psczJsonString; diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp index b3cc042c..c4567c03 100644 --- a/src/dutil/locutil.cpp +++ b/src/dutil/locutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define LocExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) +#define LocExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) +#define LocExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) +#define LocExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) +#define LocExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOCUTIL, e, x, s, __VA_ARGS__) +#define LocExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOCUTIL, g, x, s, __VA_ARGS__) + // prototypes static HRESULT ParseWxl( __in IXMLDOMDocument* pixd, @@ -63,10 +78,10 @@ extern "C" HRESULT DAPI LocProbeForFile( if (wzLanguage && *wzLanguage) { hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); - ExitOnFailure(hr, "Failed to concat base path to language."); + LocExitOnFailure(hr, "Failed to concat base path to language."); hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); - ExitOnFailure(hr, "Failed to concat loc file name to probe path."); + LocExitOnFailure(hr, "Failed to concat loc file name to probe path."); if (FileExistsEx(sczProbePath, NULL)) { @@ -81,16 +96,16 @@ extern "C" HRESULT DAPI LocProbeForFile( DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) { - ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); + LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); } hr = StrAlloc(&sczLangsBuff, cchLangs); - ExitOnFailure(hr, "Failed to allocate buffer for languages"); + LocExitOnFailure(hr, "Failed to allocate buffer for languages"); nLangs = 0; if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) { - ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); + LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); } LPWSTR szLangs = sczLangsBuff; @@ -98,14 +113,14 @@ extern "C" HRESULT DAPI LocProbeForFile( { // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value. hr = StrHexDecode(szLangs, reinterpret_cast(&langid), sizeof(langid)); - ExitOnFailure(hr, "Failed to parse langId."); + LocExitOnFailure(hr, "Failed to parse langId."); langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid)); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - ExitOnFailure(hr, "Failed to format user preferred langid."); + LocExitOnFailure(hr, "Failed to format user preferred langid."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - ExitOnFailure(hr, "Failed to concat user preferred langid file name to base path."); + LocExitOnFailure(hr, "Failed to concat user preferred langid file name to base path."); if (FileExistsEx(sczProbePath, NULL)) { @@ -117,10 +132,10 @@ extern "C" HRESULT DAPI LocProbeForFile( langid = ::GetUserDefaultUILanguage(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - ExitOnFailure(hr, "Failed to format user langid."); + LocExitOnFailure(hr, "Failed to format user langid."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - ExitOnFailure(hr, "Failed to concat user langid file name to base path."); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path."); if (FileExistsEx(sczProbePath, NULL)) { @@ -132,10 +147,10 @@ extern "C" HRESULT DAPI LocProbeForFile( langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - ExitOnFailure(hr, "Failed to format user langid (default sublang)."); + LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); if (FileExistsEx(sczProbePath, NULL)) { @@ -146,10 +161,10 @@ extern "C" HRESULT DAPI LocProbeForFile( langid = ::GetSystemDefaultUILanguage(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - ExitOnFailure(hr, "Failed to format system langid."); + LocExitOnFailure(hr, "Failed to format system langid."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - ExitOnFailure(hr, "Failed to concat system langid file name to base path."); + LocExitOnFailure(hr, "Failed to concat system langid file name to base path."); if (FileExistsEx(sczProbePath, NULL)) { @@ -161,10 +176,10 @@ extern "C" HRESULT DAPI LocProbeForFile( langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - ExitOnFailure(hr, "Failed to format user langid (default sublang)."); + LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); if (FileExistsEx(sczProbePath, NULL)) { @@ -174,7 +189,7 @@ extern "C" HRESULT DAPI LocProbeForFile( // Finally, look for the loc file in the base path. hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); - ExitOnFailure(hr, "Failed to concat loc file name to base path."); + LocExitOnFailure(hr, "Failed to concat loc file name to base path."); if (!FileExistsEx(sczProbePath, NULL)) { @@ -203,10 +218,10 @@ extern "C" HRESULT DAPI LocLoadFromFile( IXMLDOMDocument* pixd = NULL; hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd); - ExitOnFailure(hr, "Failed to load WXL file as XML document."); + LocExitOnFailure(hr, "Failed to load WXL file as XML document."); hr = ParseWxl(pixd, ppWixLoc); - ExitOnFailure(hr, "Failed to parse WXL."); + LocExitOnFailure(hr, "Failed to parse WXL."); LExit: ReleaseObject(pixd); @@ -227,16 +242,16 @@ extern "C" HRESULT DAPI LocLoadFromResource( IXMLDOMDocument* pixd = NULL; hr = ResReadData(hModule, szResource, &pvResource, &cbResource); - ExitOnFailure(hr, "Failed to read theme from resource."); + LocExitOnFailure(hr, "Failed to read theme from resource."); hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); - ExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + LocExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); hr = XmlLoadDocument(sczXml, &pixd); - ExitOnFailure(hr, "Failed to load theme resource as XML document."); + LocExitOnFailure(hr, "Failed to load theme resource as XML document."); hr = ParseWxl(pixd, ppWixLoc); - ExitOnFailure(hr, "Failed to parse WXL."); + LocExitOnFailure(hr, "Failed to parse WXL."); LExit: ReleaseObject(pixd); @@ -280,7 +295,7 @@ extern "C" HRESULT DAPI LocLocalizeString( for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) { hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText); - ExitOnFailure(hr, "Localizing string failed."); + LocExitOnFailure(hr, "Localizing string failed."); } LExit: @@ -348,15 +363,15 @@ extern "C" HRESULT DAPI LocAddString( ++pWixLoc->cLocStrings; pWixLoc->rgLocStrings = static_cast(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); - ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings."); + LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings."); LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1); hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId); - ExitOnFailure(hr, "Failed to set localization string Id."); + LocExitOnFailure(hr, "Failed to set localization string Id."); hr = StrAllocString(&pLocString->wzText, wzLocString, 0); - ExitOnFailure(hr, "Failed to set localization string Text."); + LocExitOnFailure(hr, "Failed to set localization string Text."); pLocString->bOverridable = bOverridable; @@ -376,11 +391,11 @@ static HRESULT ParseWxl( WIX_LOCALIZATION* pWixLoc = NULL; pWixLoc = static_cast(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE)); - ExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file."); + LocExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file."); // read the WixLocalization tag hr = pixd->get_documentElement(&pWxlElement); - ExitOnFailure(hr, "Failed to get localization element."); + LocExitOnFailure(hr, "Failed to get localization element."); // get the Language attribute if present pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET; @@ -389,14 +404,14 @@ static HRESULT ParseWxl( { hr = S_OK; } - ExitOnFailure(hr, "Failed to get Language value."); + LocExitOnFailure(hr, "Failed to get Language value."); // store the strings and controls in a node list hr = ParseWxlStrings(pWxlElement, pWixLoc); - ExitOnFailure(hr, "Parsing localization strings failed."); + LocExitOnFailure(hr, "Parsing localization strings failed."); hr = ParseWxlControls(pWxlElement, pWixLoc); - ExitOnFailure(hr, "Parsing localization controls failed."); + LocExitOnFailure(hr, "Parsing localization controls failed."); *ppWixLoc = pWixLoc; pWixLoc = NULL; @@ -420,27 +435,27 @@ static HRESULT ParseWxlStrings( DWORD dwIdx = 0; hr = XmlSelectNodes(pElement, L"String", &pixnl); - ExitOnLastError(hr, "Failed to get String child nodes of Wxl File."); + LocExitOnLastError(hr, "Failed to get String child nodes of Wxl File."); hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocStrings)); - ExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File."); + LocExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File."); if (0 < pWixLoc->cLocStrings) { pWixLoc->rgLocStrings = static_cast(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); - ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings."); + LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings."); while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) { hr = ParseWxlString(pixn, dwIdx, pWixLoc); - ExitOnFailure(hr, "Failed to parse localization string."); + LocExitOnFailure(hr, "Failed to parse localization string."); ++dwIdx; ReleaseNullObject(pixn); } hr = S_OK; - ExitOnFailure(hr, "Failed to enumerate all localization strings."); + LocExitOnFailure(hr, "Failed to enumerate all localization strings."); } LExit: @@ -472,27 +487,27 @@ static HRESULT ParseWxlControls( DWORD dwIdx = 0; hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl); - ExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); + LocExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocControls)); - ExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); + LocExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); if (0 < pWixLoc->cLocControls) { pWixLoc->rgLocControls = static_cast(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE)); - ExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); + LocExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) { hr = ParseWxlControl(pixn, dwIdx, pWixLoc); - ExitOnFailure(hr, "Failed to parse localized control."); + LocExitOnFailure(hr, "Failed to parse localized control."); ++dwIdx; ReleaseNullObject(pixn); } hr = S_OK; - ExitOnFailure(hr, "Failed to enumerate all localized controls."); + LocExitOnFailure(hr, "Failed to enumerate all localized controls."); } LExit: @@ -527,16 +542,16 @@ static HRESULT ParseWxlString( // Id hr = XmlGetAttribute(pixn, L"Id", &bstrText); - ExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file."); + LocExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file."); hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText); - ExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file."); + LocExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file."); ReleaseNullBSTR(bstrText); // Overrideable hr = XmlGetAttribute(pixn, L"Overridable", &bstrText); - ExitOnFailure(hr, "Failed to get Xml attribute Overridable."); + LocExitOnFailure(hr, "Failed to get Xml attribute Overridable."); if (S_OK == hr) { @@ -547,10 +562,10 @@ static HRESULT ParseWxlString( // Text hr = XmlGetText(pixn, &bstrText); - ExitOnFailure(hr, "Failed to get Xml text in Wxl file."); + LocExitOnFailure(hr, "Failed to get Xml text in Wxl file."); hr = StrAllocString(&pLocString->wzText, bstrText, 0); - ExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file."); + LocExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file."); LExit: ReleaseBSTR(bstrText); @@ -572,39 +587,39 @@ static HRESULT ParseWxlControl( // Id hr = XmlGetAttribute(pixn, L"Control", &bstrText); - ExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file."); + LocExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file."); hr = StrAllocString(&pLocControl->wzControl, bstrText, 0); - ExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file."); + LocExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file."); ReleaseNullBSTR(bstrText); // X pLocControl->nX = LOC_CONTROL_NOT_SET; hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pLocControl->nX)); - ExitOnFailure(hr, "Failed to get control X attribute."); + LocExitOnFailure(hr, "Failed to get control X attribute."); // Y pLocControl->nY = LOC_CONTROL_NOT_SET; hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pLocControl->nY)); - ExitOnFailure(hr, "Failed to get control Y attribute."); + LocExitOnFailure(hr, "Failed to get control Y attribute."); // Width pLocControl->nWidth = LOC_CONTROL_NOT_SET; hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pLocControl->nWidth)); - ExitOnFailure(hr, "Failed to get control width attribute."); + LocExitOnFailure(hr, "Failed to get control width attribute."); // Height pLocControl->nHeight = LOC_CONTROL_NOT_SET; hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pLocControl->nHeight)); - ExitOnFailure(hr, "Failed to get control height attribute."); + LocExitOnFailure(hr, "Failed to get control height attribute."); // Text hr = XmlGetText(pixn, &bstrText); - ExitOnFailure(hr, "Failed to get control text in Wxl file."); + LocExitOnFailure(hr, "Failed to get control text in Wxl file."); hr = StrAllocString(&pLocControl->wzText, bstrText, 0); - ExitOnFailure(hr, "Failed to duplicate control text in Wxl file."); + LocExitOnFailure(hr, "Failed to duplicate control text in Wxl file."); LExit: ReleaseBSTR(bstrText); diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp index 438cdbb8..35251274 100644 --- a/src/dutil/logutil.cpp +++ b/src/dutil/logutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define LoguExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) +#define LoguExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) +#define LoguExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) +#define LoguExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) +#define LoguExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOGUTIL, e, x, s, __VA_ARGS__) +#define LoguExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOGUTIL, g, x, s, __VA_ARGS__) + // globals static HMODULE LogUtil_hModule = NULL; static BOOL LogUtil_fDisabled = FALSE; @@ -110,23 +125,23 @@ extern "C" HRESULT DAPI LogOpen( if (wzExt && *wzExt) { hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog); - ExitOnFailure(hr, "Failed to create log based on current system time."); + LoguExitOnFailure(hr, "Failed to create log based on current system time."); } else { hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath); - ExitOnFailure(hr, "Failed to combine the log path."); + LoguExitOnFailure(hr, "Failed to combine the log path."); hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory); - ExitOnFailure(hr, "Failed to get log directory."); + LoguExitOnFailure(hr, "Failed to get log directory."); hr = DirEnsureExists(sczLogDirectory, NULL); - ExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory); + LoguExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory); LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == LogUtil_hLog) { - ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); } if (fAppend) @@ -152,7 +167,7 @@ extern "C" HRESULT DAPI LogOpen( if (psczLogPath) { hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0); - ExitOnFailure(hr, "Failed to copy log path."); + LoguExitOnFailure(hr, "Failed to copy log path."); } LExit: @@ -217,15 +232,15 @@ HRESULT DAPI LogRename( ReleaseFileHandle(LogUtil_hLog); hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE); - ExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath); + LoguExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath); hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0); - ExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath); + LoguExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath); LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == LogUtil_hLog) { - ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); } // Enable "append" mode by moving file pointer to the end @@ -307,7 +322,7 @@ HRESULT DAPI LogSetSpecialParams( else { hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0); - ExitOnFailure(hr, "Failed to allocate copy of special beginline string"); + LoguExitOnFailure(hr, "Failed to allocate copy of special beginline string"); } // Handle special string to be appended to every time stamp @@ -318,7 +333,7 @@ HRESULT DAPI LogSetSpecialParams( else { hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0); - ExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string"); + LoguExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string"); } // Handle special string to be appended before every full line @@ -329,7 +344,7 @@ HRESULT DAPI LogSetSpecialParams( else { hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0); - ExitOnFailure(hr, "Failed to allocate copy of special endline string"); + LoguExitOnFailure(hr, "Failed to allocate copy of special endline string"); } LExit: @@ -597,14 +612,14 @@ extern "C" HRESULT DAPI LogErrorStringArgs( LPWSTR sczMessage = NULL; hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); - ExitOnFailure(hr, "Failed to convert format string to wide character string"); + LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); // format the string as a unicode string - this is necessary to be able to include // international characters in our output string. This does have the counterintuitive effect // that the caller's "%s" is interpreted differently // (so callers should use %hs for LPSTR and %ls for LPWSTR) hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); - ExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat); + LoguExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat); hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage); @@ -636,14 +651,14 @@ extern "C" HRESULT DAPI LogErrorIdModule( WORD cStrings = 1; // guaranteed wzError is in the list hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError); - ExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError); + LoguExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError); cStrings += wzString1 ? 1 : 0; cStrings += wzString2 ? 1 : 0; cStrings += wzString3 ? 1 : 0; hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3); - ExitOnFailure(hr, "Failed to log id module."); + LoguExitOnFailure(hr, "Failed to log id module."); LExit: return hr; @@ -771,7 +786,7 @@ extern "C" HRESULT LogStringWorkRaw( if (INVALID_HANDLE_VALUE == LogUtil_hLog) { hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0); - ExitOnFailure(hr, "Failed to concatenate string to pre-init buffer"); + LoguExitOnFailure(hr, "Failed to concatenate string to pre-init buffer"); ExitFunction1(hr = S_OK); } @@ -781,7 +796,7 @@ extern "C" HRESULT LogStringWorkRaw( { if (!::WriteFile(LogUtil_hLog, reinterpret_cast(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL)) { - ExitOnLastError(hr, "Failed to write output to log: %ls - %ls", LogUtil_sczLogPath, szLogData); + LoguExitOnLastError(hr, "Failed to write output to log: %ls - %hs", LogUtil_sczLogPath, szLogData); } cbTotal += cbWrote; @@ -816,7 +831,7 @@ static HRESULT LogIdWork( if (0 == cch) { - ExitOnLastError(hr, "failed to log id: %d", dwLogId); + LoguExitOnLastError(hr, "failed to log id: %d", dwLogId); } if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1]) @@ -850,14 +865,14 @@ static HRESULT LogStringWorkArgs( LPWSTR sczMessage = NULL; hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); - ExitOnFailure(hr, "Failed to convert format string to wide character string"); + LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); // format the string as a unicode string hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); - ExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat); + LoguExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat); hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE); - ExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage); + LoguExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage); LExit: ReleaseStr(sczFormat); @@ -909,24 +924,24 @@ static HRESULT LogStringWork( hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"", dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId, LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n"); - ExitOnFailure(hr, "Failed to format line prefix."); + LoguExitOnFailure(hr, "Failed to format line prefix."); } wzLogData = scz ? scz : sczString; // Convert to UTF-8 before writing out to the log file hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8); - ExitOnFailure(hr, "Failed to convert log string to UTF-8"); + LoguExitOnFailure(hr, "Failed to convert log string to UTF-8"); if (s_vpfLogStringWorkRaw) { hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext); - ExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString); + LoguExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString); } else { hr = LogStringWorkRaw(sczMultiByte); - ExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString); + LoguExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString); } LExit: diff --git a/src/dutil/memutil.cpp b/src/dutil/memutil.cpp index 578c65ee..c805a9c0 100644 --- a/src/dutil/memutil.cpp +++ b/src/dutil/memutil.cpp @@ -1,10 +1,23 @@ -#pragma once // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - #include "precomp.h" +// Exit macros +#define MemExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) +#define MemExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) +#define MemExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) +#define MemExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) +#define MemExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MEMUTIL, e, x, s, __VA_ARGS__) +#define MemExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MEMUTIL, g, x, s, __VA_ARGS__) + + #if DEBUG static BOOL vfMemInitialized = FALSE; #endif @@ -51,7 +64,7 @@ extern "C" HRESULT DAPI MemReAllocSecure( __in LPVOID pv, __in SIZE_T cbSize, __in BOOL fZero, - __out LPVOID* ppvNew + __deref_out LPVOID* ppvNew ) { // AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); @@ -72,14 +85,14 @@ extern "C" HRESULT DAPI MemReAllocSecure( const SIZE_T cbCurrent = MemSize(pv); if (-1 == cbCurrent) { - ExitOnFailure(hr = E_INVALIDARG, "Failed to get memory size"); + MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); } // HeapReAlloc may allocate more memory than requested. const SIZE_T cbNew = MemSize(pvNew); if (-1 == cbNew) { - ExitOnFailure(hr = E_INVALIDARG, "Failed to get memory size"); + MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); } cbSize = cbNew; @@ -94,7 +107,7 @@ extern "C" HRESULT DAPI MemReAllocSecure( MemFree(pv); } } - ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory"); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory"); *ppvNew = pvNew; pvNew = NULL; @@ -129,10 +142,10 @@ extern "C" HRESULT DAPI MemReAllocArray( SIZE_T cbNew = 0; hr = ::DWordAdd(cArray, dwNewItemCount, &cNew); - ExitOnFailure(hr, "Integer overflow when calculating new element count."); + MemExitOnFailure(hr, "Integer overflow when calculating new element count."); hr = ::SIZETMult(cNew, cbArrayType, &cbNew); - ExitOnFailure(hr, "Integer overflow when calculating new block size."); + MemExitOnFailure(hr, "Integer overflow when calculating new block size."); if (*ppvArray) { @@ -140,7 +153,7 @@ extern "C" HRESULT DAPI MemReAllocArray( if (cbCurrent < cbNew) { pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); - ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array."); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array."); *ppvArray = pvNew; } @@ -148,7 +161,7 @@ extern "C" HRESULT DAPI MemReAllocArray( else { pvNew = MemAlloc(cbNew, TRUE); - ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); *ppvArray = pvNew; } @@ -159,7 +172,7 @@ LExit: extern "C" HRESULT DAPI MemEnsureArraySize( - __deref_out_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, __in DWORD cArray, __in SIZE_T cbArrayType, __in DWORD dwGrowthCount @@ -171,10 +184,10 @@ extern "C" HRESULT DAPI MemEnsureArraySize( SIZE_T cbNew = 0; hr = ::DWordAdd(cArray, dwGrowthCount, &cNew); - ExitOnFailure(hr, "Integer overflow when calculating new element count."); + MemExitOnFailure(hr, "Integer overflow when calculating new element count."); hr = ::SIZETMult(cNew, cbArrayType, &cbNew); - ExitOnFailure(hr, "Integer overflow when calculating new block size."); + MemExitOnFailure(hr, "Integer overflow when calculating new block size."); if (*ppvArray) { @@ -183,7 +196,7 @@ extern "C" HRESULT DAPI MemEnsureArraySize( if (cbCurrent < cbUsed) { pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); - ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger."); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger."); *ppvArray = pvNew; } @@ -191,7 +204,7 @@ extern "C" HRESULT DAPI MemEnsureArraySize( else { pvNew = MemAlloc(cbNew, TRUE); - ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); *ppvArray = pvNew; } @@ -202,7 +215,7 @@ LExit: extern "C" HRESULT DAPI MemInsertIntoArray( - __deref_out_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, __in DWORD dwInsertIndex, __in DWORD cInsertItems, __in DWORD cExistingArray, @@ -220,7 +233,7 @@ extern "C" HRESULT DAPI MemInsertIntoArray( } hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount); - ExitOnFailure(hr, "Failed to resize array while inserting items"); + MemExitOnFailure(hr, "Failed to resize array while inserting items"); pbArray = reinterpret_cast(*ppvArray); for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i) @@ -236,7 +249,7 @@ LExit: } extern "C" void DAPI MemRemoveFromArray( - __inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID pvArray, + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, __in DWORD dwRemoveIndex, __in DWORD cRemoveItems, __in DWORD cExistingArray, @@ -261,7 +274,7 @@ extern "C" void DAPI MemRemoveFromArray( } extern "C" void DAPI MemArraySwapItems( - __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __inout_bcount(cbArrayType) LPVOID pvArray, __in DWORD dwIndex1, __in DWORD dwIndex2, __in SIZE_T cbArrayType diff --git a/src/dutil/metautil.cpp b/src/dutil/metautil.cpp index 612b1127..109cd31e 100644 --- a/src/dutil/metautil.cpp +++ b/src/dutil/metautil.cpp @@ -9,6 +9,21 @@ #include "metautil.h" +// Exit macros +#define MetaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) +#define MetaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) +#define MetaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) +#define MetaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) +#define MetaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_METAUTIL, e, x, s, __VA_ARGS__) +#define MetaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_METAUTIL, g, x, s, __VA_ARGS__) + + // prototypes static void Sort( __in_ecount(cArray) DWORD dwArray[], @@ -75,7 +90,7 @@ extern "C" HRESULT DAPI MetaFindWebBase( hr = S_FALSE; // didn't find anything, try next one continue; } - ExitOnFailure(hr, "failed to get key from metabase while searching for web servers"); + MetaExitOnFailure(hr, "failed to get key from metabase while searching for web servers"); // if we have an IIsWebServer store the key if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) @@ -83,7 +98,7 @@ extern "C" HRESULT DAPI MetaFindWebBase( hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress); if (MD_ERROR_DATA_NOT_FOUND == hr) hr = S_FALSE; - ExitOnFailure(hr, "failed to get address from metabase while searching for web servers"); + MetaExitOnFailure(hr, "failed to get address from metabase while searching for web servers"); // break down the first address into parts pwzIPExists = reinterpret_cast(mrAddress.pbMDData); @@ -111,7 +126,7 @@ extern "C" HRESULT DAPI MetaFindWebBase( { // if the passed in buffer wasn't big enough hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey); - ExitOnFailure(hr, "failed to copy in web base: %ls", wzKey); + MetaExitOnFailure(hr, "failed to copy in web base: %ls", wzKey); fFound = TRUE; break; @@ -182,7 +197,7 @@ extern "C" HRESULT DAPI MetaFindFreeWebBase( hr = S_FALSE; // didn't find anything, try next one continue; } - ExitOnFailure(hr, "failed to get key from metabase while searching for free web root"); + MetaExitOnFailure(hr, "failed to get key from metabase while searching for free web root"); // if we have a IIsWebServer get the address information if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast(mr.pbMDData))) @@ -190,7 +205,7 @@ extern "C" HRESULT DAPI MetaFindFreeWebBase( if (cSubKeys >= countof(dwSubKeys)) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites"); + MetaExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites"); } dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10); @@ -201,7 +216,7 @@ extern "C" HRESULT DAPI MetaFindFreeWebBase( if (E_NOMOREITEMS == hr) hr = S_OK; - ExitOnFailure(hr, "failed to find free web root"); + MetaExitOnFailure(hr, "failed to find free web root"); // find the lowest free web root dwKey = 1; @@ -270,18 +285,18 @@ extern "C" HRESULT DAPI MetaGetValue( if (!piMetabase) { hr = ::CoInitialize(NULL); - ExitOnFailure(hr, "failed to initialize COM"); + MetaExitOnFailure(hr, "failed to initialize COM"); fInitialized = TRUE; hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); - ExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object"); + MetaExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object"); } if (!pmr->pbMDData) { pmr->dwMDDataLen = 256; pmr->pbMDData = static_cast(MemAlloc(pmr->dwMDDataLen, TRUE)); - ExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); + MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); } else // set the size of the data to the actual size of the memory pmr->dwMDDataLen = (DWORD)MemSize(pmr->pbMDData); @@ -291,12 +306,12 @@ extern "C" HRESULT DAPI MetaGetValue( { pmr->dwMDDataLen = cbRequired; BYTE* pb = static_cast(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE)); - ExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value"); + MetaExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value"); pmr->pbMDData = pb; hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); } - ExitOnFailure(hr, "failed to get metabase data"); + MetaExitOnFailure(hr, "failed to get metabase data"); LExit: if (fInitialized) diff --git a/src/dutil/monutil.cpp b/src/dutil/monutil.cpp index 6f280538..6a7f0596 100644 --- a/src/dutil/monutil.cpp +++ b/src/dutil/monutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define MonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) +#define MonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) +#define MonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) +#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) +#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__) +#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__) + const int MON_THREAD_GROWTH = 5; const int MON_ARRAY_GROWTH = 40; const int MON_MAX_MONITORS_PER_THREAD = 63; @@ -218,10 +233,10 @@ static void MonRequestDestroy( __in MON_REQUEST *pRequest ); static void MonAddMessageDestroy( - __in MON_ADD_MESSAGE *pMessage + __in_opt MON_ADD_MESSAGE *pMessage ); static void MonRemoveMessageDestroy( - __in MON_REMOVE_MESSAGE *pMessage + __in_opt MON_REMOVE_MESSAGE *pMessage ); static BOOL GetRecursiveFlag( __in MON_REQUEST *pRequest, @@ -262,7 +277,7 @@ static HRESULT UpdateWaitStatus( __in HRESULT hrNewStatus, __inout MON_WAITER_CONTEXT *pWaiterContext, __in DWORD dwRequestIndex, - __out DWORD *pdwNewRequestIndex + __out_opt DWORD *pdwNewRequestIndex ); extern "C" HRESULT DAPI MonCreate( @@ -277,11 +292,11 @@ extern "C" HRESULT DAPI MonCreate( HRESULT hr = S_OK; DWORD dwRetries = MON_THREAD_INIT_RETRIES; - ExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor"); + MonExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor"); // Allocate the struct *pHandle = static_cast(MemAlloc(sizeof(MON_STRUCT), TRUE)); - ExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object"); + MonExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object"); MON_STRUCT *pm = static_cast(*pHandle); @@ -294,7 +309,7 @@ extern "C" HRESULT DAPI MonCreate( pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId); if (!pm->hCoordinatorThread) { - ExitWithLastError(hr, "Failed to create waiter thread."); + MonExitWithLastError(hr, "Failed to create waiter thread."); } // 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. @@ -307,7 +322,7 @@ extern "C" HRESULT DAPI MonCreate( if (0 == dwRetries) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); } LExit: @@ -329,13 +344,13 @@ extern "C" HRESULT DAPI MonAddDirectory( MON_ADD_MESSAGE *pMessage = NULL; hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0); - ExitOnFailure(hr, "Failed to convert directory string to UNC path"); + MonExitOnFailure(hr, "Failed to convert directory string to UNC path"); hr = PathBackslashTerminate(&sczOriginalPathRequest); - ExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); - ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\') { @@ -356,7 +371,7 @@ extern "C" HRESULT DAPI MonAddDirectory( hr = S_OK; hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0); - ExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest); + MonExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest); } pMessage->handle = INVALID_HANDLE_VALUE; @@ -369,14 +384,14 @@ extern "C" HRESULT DAPI MonAddDirectory( sczOriginalPathRequest = NULL; hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); - ExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory); + MonExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory); if (0 < pMessage->request.cPathHierarchy) { pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) { - ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); } pMessage = NULL; } @@ -405,16 +420,16 @@ extern "C" HRESULT DAPI MonAddRegKey( MON_ADD_MESSAGE *pMessage = NULL; hr = StrAllocString(&sczSubKey, wzSubKey, 0); - ExitOnFailure(hr, "Failed to copy subkey string"); + MonExitOnFailure(hr, "Failed to copy subkey string"); hr = PathBackslashTerminate(&sczSubKey); - ExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); - ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor"); + MonExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor"); pMessage->request.type = MON_REGKEY; pMessage->request.regkey.hkRoot = hkRoot; @@ -425,16 +440,16 @@ extern "C" HRESULT DAPI MonAddRegKey( pMessage->request.pvContext = pvRegKeyContext; hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); - ExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey); + MonExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey); if (0 < pMessage->request.cPathHierarchy) { pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); - ExitOnFailure(hr, "Failed to initiate wait"); + MonExitOnFailure(hr, "Failed to initiate wait"); if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) { - ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey); + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey); } pMessage = NULL; } @@ -458,23 +473,23 @@ extern "C" HRESULT DAPI MonRemoveDirectory( MON_REMOVE_MESSAGE *pMessage = NULL; hr = StrAllocString(&sczDirectory, wzDirectory, 0); - ExitOnFailure(hr, "Failed to copy directory string"); + MonExitOnFailure(hr, "Failed to copy directory string"); hr = PathBackslashTerminate(&sczDirectory); - ExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); pMessage->type = MON_DIRECTORY; pMessage->fRecursive = fRecursive; hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0); - ExitOnFailure(hr, "Failed to allocate copy of directory string"); + MonExitOnFailure(hr, "Failed to allocate copy of directory string"); if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) { - ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); } pMessage = NULL; @@ -498,13 +513,13 @@ extern "C" HRESULT DAPI MonRemoveRegKey( MON_REMOVE_MESSAGE *pMessage = NULL; hr = StrAllocString(&sczSubKey, wzSubKey, 0); - ExitOnFailure(hr, "Failed to copy subkey string"); + MonExitOnFailure(hr, "Failed to copy subkey string"); hr = PathBackslashTerminate(&sczSubKey); - ExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); pMessage->type = MON_REGKEY; pMessage->regkey.hkRoot = hkRoot; @@ -512,11 +527,11 @@ extern "C" HRESULT DAPI MonRemoveRegKey( pMessage->fRecursive = fRecursive; hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0); - ExitOnFailure(hr, "Failed to allocate copy of directory string"); + MonExitOnFailure(hr, "Failed to allocate copy of directory string"); if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) { - ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey); + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey); } pMessage = NULL; @@ -543,7 +558,7 @@ extern "C" void DAPI MonDestroy( // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up er = ERROR_SUCCESS; } - ExitOnWin32Error(er, hr, "Failed to send message to background thread to halt"); + MonExitOnWin32Error(er, hr, "Failed to send message to background thread to halt"); } if (pm->hCoordinatorThread) @@ -577,10 +592,10 @@ static void MonRequestDestroy( } static void MonAddMessageDestroy( - __in MON_ADD_MESSAGE *pMessage + __in_opt MON_ADD_MESSAGE *pMessage ) { - if (NULL != pMessage) + if (pMessage) { MonRequestDestroy(&pMessage->request); if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle) @@ -597,10 +612,10 @@ static void MonAddMessageDestroy( } static void MonRemoveMessageDestroy( - __in MON_REMOVE_MESSAGE *pMessage + __in_opt MON_REMOVE_MESSAGE *pMessage ) { - if (NULL != pMessage) + if (pMessage) { switch (pMessage->type) { @@ -642,17 +657,17 @@ static DWORD WINAPI CoordinatorThread( pm->fCoordinatorThreadMessageQueueInitialized = TRUE; hr = CreateMonWindow(pm, &pm->hwnd); - ExitOnFailure(hr, "Failed to create window for status update thread"); + MonExitOnFailure(hr, "Failed to create window for status update thread"); ::WSAStartup(MAKEWORD(2, 2), &wsaData); hr = WaitForNetworkChanges(&hMonitor, pm); - ExitOnFailure(hr, "Failed to wait for network changes"); + MonExitOnFailure(hr, "Failed to wait for network changes"); uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL); if (0 == uTimerSuccessfulNetworkRetry) { - ExitWithLastError(hr, "Failed to set timer for network successful retry"); + MonExitWithLastError(hr, "Failed to set timer for network successful retry"); } while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) @@ -660,7 +675,7 @@ static DWORD WINAPI CoordinatorThread( if (-1 == fRet) { hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Unexpected return value from message pump."); + MonExitOnRootFailure(hr, "Unexpected return value from message pump."); } else { @@ -684,12 +699,12 @@ static DWORD WINAPI CoordinatorThread( else { hr = MemEnsureArraySize(reinterpret_cast(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH); - ExitOnFailure(hr, "Failed to grow waiter thread array size"); + MonExitOnFailure(hr, "Failed to grow waiter thread array size"); ++pm->cWaiterThreads; dwThreadIndex = pm->cWaiterThreads - 1; pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE)); - ExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct"); + MonExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct"); pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId(); pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral; @@ -698,16 +713,16 @@ static DWORD WINAPI CoordinatorThread( pWaiterContext->pvContext = pm->pvContext; hr = MemEnsureArraySize(reinterpret_cast(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0); - ExitOnFailure(hr, "Failed to allocate first handle"); + MonExitOnFailure(hr, "Failed to allocate first handle"); pWaiterContext->cHandles = 1; pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL); - ExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event"); + MonExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event"); pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId); if (!pWaiterContext->hWaiterThread) { - ExitWithLastError(hr, "Failed to create waiter thread."); + MonExitWithLastError(hr, "Failed to create waiter thread."); } dwRetries = MON_THREAD_INIT_RETRIES; @@ -720,19 +735,19 @@ static DWORD WINAPI CoordinatorThread( if (0 == dwRetries) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); } } ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount; if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0)) { - ExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); } if (!::SetEvent(pWaiterContext->rgHandles[0])) { - ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message"); + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message"); } break; @@ -746,17 +761,17 @@ static DWORD WINAPI CoordinatorThread( pRemoveMessage = reinterpret_cast(msg.wParam); hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage); - ExitOnFailure(hr, "Failed to duplicate remove message"); + MonExitOnFailure(hr, "Failed to duplicate remove message"); if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pTempRemoveMessage), msg.lParam)) { - ExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); } pTempRemoveMessage = NULL; if (!::SetEvent(pWaiterContext->rgHandles[0])) { - ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message"); + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message"); } } MonRemoveMessageDestroy(pRemoveMessage); @@ -774,7 +789,7 @@ static DWORD WINAPI CoordinatorThread( { if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) { - ExitWithLastError(hr, "Failed to send message to waiter thread to stop"); + MonExitWithLastError(hr, "Failed to send message to waiter thread to stop"); } MemRemoveFromArray(reinterpret_cast(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE); --pm->cWaiterThreads; @@ -790,7 +805,7 @@ static DWORD WINAPI CoordinatorThread( uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL); if (0 == uTimerFailedNetworkRetry) { - ExitWithLastError(hr, "Failed to set timer for network fail retry"); + MonExitWithLastError(hr, "Failed to set timer for network fail retry"); } } ++dwFailingNetworkWaits; @@ -802,7 +817,7 @@ static DWORD WINAPI CoordinatorThread( { if (!::KillTimer(NULL, uTimerFailedNetworkRetry)) { - ExitWithLastError(hr, "Failed to kill timer for network fail retry"); + MonExitWithLastError(hr, "Failed to kill timer for network fail retry"); } uTimerFailedNetworkRetry = 0; } @@ -810,7 +825,7 @@ static DWORD WINAPI CoordinatorThread( case MON_MESSAGE_NETWORK_STATUS_UPDATE: hr = WaitForNetworkChanges(&hMonitor, pm); - ExitOnFailure(hr, "Failed to re-wait for network changes"); + MonExitOnFailure(hr, "Failed to re-wait for network changes"); // Propagate any network status update messages to all waiter threads for (DWORD i = 0; i < pm->cWaiterThreads; ++i) @@ -819,12 +834,12 @@ static DWORD WINAPI CoordinatorThread( if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0)) { - ExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); } if (!::SetEvent(pWaiterContext->rgHandles[0])) { - ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); } } break; @@ -837,12 +852,12 @@ static DWORD WINAPI CoordinatorThread( if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0)) { - ExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); } if (!::SetEvent(pWaiterContext->rgHandles[0])) { - ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); } } break; @@ -861,12 +876,12 @@ static DWORD WINAPI CoordinatorThread( if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam)) { - ExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update"); + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update"); } if (!::SetEvent(pWaiterContext->rgHandles[0])) { - ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message"); + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message"); } } break; @@ -998,7 +1013,7 @@ static HRESULT InitiateWait( { continue; } - ExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]); + MonExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]); } else { @@ -1013,7 +1028,7 @@ static HRESULT InitiateWait( { continue; } - ExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + MonExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE); ReleaseRegKey(hk); @@ -1024,7 +1039,7 @@ static HRESULT InitiateWait( } else { - ExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + MonExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); fHandleFound = TRUE; } @@ -1062,7 +1077,7 @@ static HRESULT InitiateWait( } } while (fRedo); - ExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]); + MonExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]); if (MON_DIRECTORY == pRequest->type) { @@ -1141,7 +1156,7 @@ static DWORD WINAPI WaiterThread( } hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH); - ExitOnFailure(hr, "Failed to insert additional handle"); + MonExitOnFailure(hr, "Failed to insert additional handle"); ++pWaiterContext->cHandles; // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL @@ -1151,7 +1166,7 @@ static DWORD WINAPI WaiterThread( } hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH); - ExitOnFailure(hr, "Failed to insert additional request struct"); + MonExitOnFailure(hr, "Failed to insert additional request struct"); ++pWaiterContext->cRequests; pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request; @@ -1172,10 +1187,10 @@ static DWORD WINAPI WaiterThread( } else { - ExitOnFailure(hr, "Failed to find request index for remove message"); + MonExitOnFailure(hr, "Failed to find request index for remove message"); hr = RemoveRequest(pWaiterContext, dwRequestIndex); - ExitOnFailure(hr, "Failed to remove request after request from coordinator thread."); + MonExitOnFailure(hr, "Failed to remove request after request from coordinator thread."); } MonRemoveMessageDestroy(pRemoveMessage); @@ -1204,7 +1219,7 @@ static DWORD WINAPI WaiterThread( hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - ExitOnFailure(hr, "Failed to update wait status"); + MonExitOnFailure(hr, "Failed to update wait status"); hrTemp = S_OK; if (dwNewRequestIndex != i) @@ -1239,7 +1254,7 @@ static DWORD WINAPI WaiterThread( hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - ExitOnFailure(hr, "Failed to update wait status"); + MonExitOnFailure(hr, "Failed to update wait status"); hrTemp = S_OK; if (dwNewRequestIndex != i) @@ -1274,7 +1289,7 @@ static DWORD WINAPI WaiterThread( hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - ExitOnFailure(hr, "Failed to update wait status"); + MonExitOnFailure(hr, "Failed to update wait status"); hrTemp = S_OK; if (dwNewRequestIndex != i) @@ -1311,7 +1326,7 @@ static DWORD WINAPI WaiterThread( } hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - ExitOnFailure(hr, "Failed to update wait status"); + MonExitOnFailure(hr, "Failed to update wait status"); hrTemp = S_OK; if (dwNewRequestIndex != i) @@ -1354,7 +1369,7 @@ static DWORD WINAPI WaiterThread( hrTemp = E_PATHNOTFOUND; hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - ExitOnFailure(hr, "Failed to update wait status"); + MonExitOnFailure(hr, "Failed to update wait status"); hrTemp = S_OK; break; } @@ -1385,7 +1400,7 @@ static DWORD WINAPI WaiterThread( // Initiate re-waits before we notify callback, to ensure we don't miss a single update hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1); hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex); - ExitOnFailure(hr, "Failed to update wait status"); + MonExitOnFailure(hr, "Failed to update wait status"); hrTemp = S_OK; // 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 @@ -1413,7 +1428,7 @@ static DWORD WINAPI WaiterThread( } else if (WAIT_TIMEOUT != dwRet) { - ExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); + MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); } // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire @@ -1432,7 +1447,7 @@ static DWORD WINAPI WaiterThread( { Assert(FALSE); hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); - ExitOnFailure(hr, "Phantom pending fires were found!"); + MonExitOnFailure(hr, "Phantom pending fires were found!"); } --cRequestsPendingBeforeLoop; @@ -1470,13 +1485,13 @@ static DWORD WINAPI WaiterThread( { Assert(FALSE); hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA); - ExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait); + MonExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait); } if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait) { Assert(FALSE); hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT); - ExitOnFailure(hr, "Pending fires exist, but wait was infinite", cRequestsPendingBeforeLoop); + MonExitOnFailure(hr, "Pending fires exist (%u), but wait was infinite", cRequestsPendingBeforeLoop); } } } while (fContinue); @@ -1651,7 +1666,7 @@ static HRESULT RemoveRequest( // Notify coordinator thread that a wait was removed if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast(::GetCurrentThreadId()), 0)) { - ExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed."); + MonExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed."); } LExit: @@ -1684,7 +1699,7 @@ static HRESULT DuplicateRemoveMessage( HRESULT hr = S_OK; *ppMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - ExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message"); + MonExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message"); (*ppMessage)->type = pMessage->type; (*ppMessage)->fRecursive = pMessage->fRecursive; @@ -1693,13 +1708,13 @@ static HRESULT DuplicateRemoveMessage( { case MON_DIRECTORY: hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0); - ExitOnFailure(hr, "Failed to copy directory"); + MonExitOnFailure(hr, "Failed to copy directory"); break; case MON_REGKEY: (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot; (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness; hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0); - ExitOnFailure(hr, "Failed to copy subkey"); + MonExitOnFailure(hr, "Failed to copy subkey"); break; default: Assert(false); @@ -1764,7 +1779,7 @@ static LRESULT CALLBACK MonWndProc( // This drive had a status update, so send it out to all threads if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast(chDrive), static_cast(fArrival))) { - ExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE"); + MonExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE"); } } dwUnitMask >>= 1; @@ -1773,7 +1788,7 @@ static LRESULT CALLBACK MonWndProc( if (chDrive == 'z') { hr = E_UNEXPECTED; - ExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask); + MonExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask); } } } @@ -1785,7 +1800,7 @@ static LRESULT CALLBACK MonWndProc( if (!pm) { hr = E_POINTER; - ExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored"); + MonExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored"); } fReturnTrue = TRUE; @@ -1796,7 +1811,7 @@ static LRESULT CALLBACK MonWndProc( // 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 // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed"); + MonExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed"); pHandle = reinterpret_cast(lParam); pm->internalWait.pvContext = pHandle->dbch_handle; @@ -1808,12 +1823,12 @@ static LRESULT CALLBACK MonWndProc( if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast(&pm->internalWait), static_cast(pm->internalWait.dwSendIteration))) { - ExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove"); + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove"); } if (!::SetEvent(pWaiterContext->rgHandles[0])) { - ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message"); + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message"); } } @@ -1833,7 +1848,7 @@ static LRESULT CALLBACK MonWndProc( } else { - ExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); + MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); } ++pm->internalWait.dwSendIteration; } @@ -1871,12 +1886,12 @@ static HRESULT CreateMonWindow( { if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError()) { - ExitWithLastError(hr, "Failed to register MonUtil window class."); + MonExitWithLastError(hr, "Failed to register MonUtil window class."); } } *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm); - ExitOnNullWithLastError(*pHwnd, hr, "Failed to create window."); + MonExitOnNullWithLastError(*pHwnd, hr, "Failed to create window."); // 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 // SWP_NOACTIVATE is important so the currently active window doesn't lose focus @@ -1909,7 +1924,7 @@ static HRESULT WaitForNetworkChanges( if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor)) { hr = HRESULT_FROM_WIN32(::WSAGetLastError()); - ExitOnFailure(hr, "WSALookupServiceBegin() failed"); + MonExitOnFailure(hr, "WSALookupServiceBegin() failed"); } wsaCompletion.Type = NSP_NOTIFY_HWND; @@ -1923,7 +1938,7 @@ static HRESULT WaitForNetworkChanges( { hr = E_FAIL; } - ExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError()); + MonExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError()); } LExit: @@ -1960,7 +1975,7 @@ static HRESULT UpdateWaitStatus( // If it's a network wait, notify coordinator thread that a network wait is failing if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0)) { - ExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail"); + MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail"); } // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle @@ -1981,7 +1996,7 @@ static HRESULT UpdateWaitStatus( // If it's a network wait, notify coordinator thread that a network wait is succeeding again if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0)) { - ExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again"); + MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again"); } --pWaiterContext->cRequestsFailing; diff --git a/src/dutil/osutil.cpp b/src/dutil/osutil.cpp index 8834cd30..880ec3ea 100644 --- a/src/dutil/osutil.cpp +++ b/src/dutil/osutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define OsExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) +#define OsExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) +#define OsExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) +#define OsExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) +#define OsExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_OSUTIL, e, x, s, __VA_ARGS__) +#define OsExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_OSUTIL, g, x, s, __VA_ARGS__) + typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; @@ -127,7 +142,7 @@ extern "C" HRESULT DAPI OsIsRunningPrivileged( if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) { - ExitOnLastError(hr, "Failed to open process token."); + OsExitOnLastError(hr, "Failed to open process token."); } if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) @@ -142,7 +157,7 @@ extern "C" HRESULT DAPI OsIsRunningPrivileged( { er = ERROR_SUCCESS; } - ExitOnWin32Error(er, hr, "Failed to get process token information."); + OsExitOnWin32Error(er, hr, "Failed to get process token information."); // Fallback to this check for some OS's (like XP) *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); @@ -180,14 +195,14 @@ extern "C" HRESULT DAPI OsIsUacEnabled( { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to open system policy key to detect UAC."); + OsExitOnFailure(hr, "Failed to open system policy key to detect UAC."); hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled); if (E_FILENOTFOUND == hr) { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to read registry value to detect UAC."); + OsExitOnFailure(hr, "Failed to read registry value to detect UAC."); *pfUacEnabled = (0 != dwUacEnabled); @@ -215,12 +230,12 @@ HRESULT DAPI OsRtlGetVersion( hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll); if (E_MODNOTFOUND == hr) { - ExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); + OsExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); } - ExitOnFailure(hr, "Failed to load ntdll.dll."); + OsExitOnFailure(hr, "Failed to load ntdll.dll."); pfnRtlGetVersion = reinterpret_cast(::GetProcAddress(hNtdll, "RtlGetVersion")); - ExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); + OsExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); hr = static_cast(pfnRtlGetVersion(&vovix)); diff --git a/src/dutil/path2utl.cpp b/src/dutil/path2utl.cpp index 8f5f03a1..ff3a946d 100644 --- a/src/dutil/path2utl.cpp +++ b/src/dutil/path2utl.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) +#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) + + DAPI_(HRESULT) PathCanonicalizePath( __in_z LPCWSTR wzPath, __deref_out_z LPWSTR* psczCanonicalized @@ -12,7 +27,7 @@ DAPI_(HRESULT) PathCanonicalizePath( int cch = MAX_PATH + 1; hr = StrAlloc(psczCanonicalized, cch); - ExitOnFailure(hr, "Failed to allocate string for the canonicalized path."); + PathExitOnFailure(hr, "Failed to allocate string for the canonicalized path."); if (::PathCanonicalizeW(*psczCanonicalized, wzPath)) { @@ -39,10 +54,10 @@ DAPI_(HRESULT) PathDirectoryContainsPath( LPWSTR sczOriginalDirectory = NULL; hr = PathCanonicalizePath(wzPath, &sczOriginalPath); - ExitOnFailure(hr, "Failed to canonicalize the path."); + PathExitOnFailure(hr, "Failed to canonicalize the path."); hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory); - ExitOnFailure(hr, "Failed to canonicalize the directory."); + PathExitOnFailure(hr, "Failed to canonicalize the directory."); if (!sczOriginalPath || !*sczOriginalPath) { diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp index d8894756..ec338f71 100644 --- a/src/dutil/pathutil.cpp +++ b/src/dutil/pathutil.cpp @@ -2,11 +2,26 @@ #include "precomp.h" + +// Exit macros +#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) +#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) + #define PATH_GOOD_ENOUGH 64 DAPI_(HRESULT) PathCommandLineAppend( - __deref_out_z LPWSTR* psczCommandLine, + __deref_inout_z LPWSTR* psczCommandLine, __in_z LPCWSTR wzArgument ) { @@ -41,7 +56,7 @@ DAPI_(HRESULT) PathCommandLineAppend( if (fRequiresQuoting) { hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. - ExitOnFailure(hr, "Failed to allocate argument to be quoted."); + PathExitOnFailure(hr, "Failed to allocate argument to be quoted."); LPCWSTR pwz = wzArgument; LPWSTR pwzQuoted = sczQuotedArg; @@ -94,11 +109,11 @@ DAPI_(HRESULT) PathCommandLineAppend( if (*psczCommandLine && **psczCommandLine) { hr = StrAllocConcat(psczCommandLine, L" ", 0); - ExitOnFailure(hr, "Failed to append space to command line with existing data."); + PathExitOnFailure(hr, "Failed to append space to command line with existing data."); } hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); - ExitOnFailure(hr, "Failed to copy command line argument."); + PathExitOnFailure(hr, "Failed to copy command line argument."); LExit: ReleaseStr(sczQuotedArg); @@ -162,7 +177,7 @@ DAPI_(LPCWSTR) PathExtension( DAPI_(HRESULT) PathGetDirectory( __in_z LPCWSTR wzPath, - __out LPWSTR *psczDirectory + __out_z LPWSTR *psczDirectory ) { HRESULT hr = S_OK; @@ -193,7 +208,7 @@ DAPI_(HRESULT) PathGetDirectory( } hr = StrAllocString(psczDirectory, wzPath, cchDirectory); - ExitOnFailure(hr, "Failed to copy directory."); + PathExitOnFailure(hr, "Failed to copy directory."); LExit: return hr; @@ -223,28 +238,28 @@ DAPI_(HRESULT) PathExpand( cchExpandedPath = PATH_GOOD_ENOUGH; hr = StrAlloc(&sczExpandedPath, cchExpandedPath); - ExitOnFailure(hr, "Failed to allocate space for expanded path."); + PathExitOnFailure(hr, "Failed to allocate space for expanded path."); cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); if (0 == cch) { - ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); } else if (cchExpandedPath < cch) { cchExpandedPath = cch; hr = StrAlloc(&sczExpandedPath, cchExpandedPath); - ExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + PathExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); if (0 == cch) { - ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); } else if (cchExpandedPath < cch) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + PathExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); } } @@ -255,10 +270,10 @@ DAPI_(HRESULT) PathExpand( { hr = S_OK; } - ExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); + PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); hr = StrMaxLength(sczExpandedPath, reinterpret_cast(&cchExpandedPath)); - ExitOnFailure(hr, "Failed to get max length of expanded path."); + PathExitOnFailure(hr, "Failed to get max length of expanded path."); } } @@ -272,35 +287,35 @@ DAPI_(HRESULT) PathExpand( DWORD cchFullPath = PATH_GOOD_ENOUGH < cchExpandedPath ? cchExpandedPath : PATH_GOOD_ENOUGH; hr = StrAlloc(&sczFullPath, cchFullPath); - ExitOnFailure(hr, "Failed to allocate space for full path."); + PathExitOnFailure(hr, "Failed to allocate space for full path."); cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); if (0 == cch) { - ExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); } else if (cchFullPath < cch) { cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed hr = StrAlloc(&sczFullPath, cchFullPath); - ExitOnFailure(hr, "Failed to re-allocate more space for full path."); + PathExitOnFailure(hr, "Failed to re-allocate more space for full path."); cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); if (0 == cch) { - ExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); } else if (cchFullPath < cch) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + PathExitOnRootFailure(hr, "Failed to allocate buffer for full path."); } } if (MAX_PATH < cch) { hr = PathPrefix(&sczFullPath); - ExitOnFailure(hr, "Failed to prefix long path after expanding."); + PathExitOnFailure(hr, "Failed to prefix long path after expanding."); } } else @@ -310,7 +325,7 @@ DAPI_(HRESULT) PathExpand( } hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); - ExitOnFailure(hr, "Failed to copy relative path into full path."); + PathExitOnFailure(hr, "Failed to copy relative path into full path."); LExit: ReleaseStr(sczFullPath); @@ -336,7 +351,7 @@ DAPI_(HRESULT) PathPrefix( L'\\' == wzFullPath[2]) // normal path { hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); - ExitOnFailure(hr, "Failed to add prefix to file path."); + PathExitOnFailure(hr, "Failed to add prefix to file path."); } else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC { @@ -344,18 +359,18 @@ DAPI_(HRESULT) PathPrefix( if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) { hr = StrSize(*psczFullPath, &cbFullPath); - ExitOnFailure(hr, "Failed to get size of full path."); + PathExitOnFailure(hr, "Failed to get size of full path."); memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); - ExitOnFailure(hr, "Failed to add prefix to UNC path."); + PathExitOnFailure(hr, "Failed to add prefix to UNC path."); } } else { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); + PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); } LExit: @@ -372,7 +387,7 @@ DAPI_(HRESULT) PathFixedBackslashTerminate( size_t cchLength = 0; hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); - ExitOnFailure(hr, "Failed to get length of path."); + PathExitOnFailure(hr, "Failed to get length of path."); if (cchLength >= cchPath) { @@ -400,15 +415,15 @@ DAPI_(HRESULT) PathBackslashTerminate( size_t cchLength = 0; hr = StrMaxLength(*psczPath, &cchPath); - ExitOnFailure(hr, "Failed to get size of path string."); + PathExitOnFailure(hr, "Failed to get size of path string."); hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); - ExitOnFailure(hr, "Failed to get length of path."); + PathExitOnFailure(hr, "Failed to get length of path."); if (L'\\' != (*psczPath)[cchLength - 1]) { hr = StrAllocConcat(psczPath, L"\\", 1); - ExitOnFailure(hr, "Failed to concat backslash onto string."); + PathExitOnFailure(hr, "Failed to concat backslash onto string."); } LExit: @@ -427,12 +442,12 @@ DAPI_(HRESULT) PathForCurrentProcess( do { hr = StrAlloc(psczFullPath, cch); - ExitOnFailure(hr, "Failed to allocate string for module path."); + PathExitOnFailure(hr, "Failed to allocate string for module path."); DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); if (0 == cchRequired) { - ExitWithLastError(hr, "Failed to get path for executing process."); + PathExitWithLastError(hr, "Failed to get path for executing process."); } else if (cchRequired == cch) { @@ -457,15 +472,15 @@ DAPI_(HRESULT) PathRelativeToModule( ) { HRESULT hr = PathForCurrentProcess(psczFullPath, hModule); - ExitOnFailure(hr, "Failed to get current module path."); + PathExitOnFailure(hr, "Failed to get current module path."); hr = PathGetDirectory(*psczFullPath, psczFullPath); - ExitOnFailure(hr, "Failed to get current module directory."); + PathExitOnFailure(hr, "Failed to get current module directory."); if (wzFileName) { hr = PathConcat(*psczFullPath, wzFileName, psczFullPath); - ExitOnFailure(hr, "Failed to append filename."); + PathExitOnFailure(hr, "Failed to append filename."); } LExit: @@ -496,16 +511,16 @@ DAPI_(HRESULT) PathCreateTempFile( if (wzDirectory && *wzDirectory) { hr = StrAllocString(&sczTempPath, wzDirectory, 0); - ExitOnFailure(hr, "Failed to copy temp path."); + PathExitOnFailure(hr, "Failed to copy temp path."); } else { hr = StrAlloc(&sczTempPath, cchTempPath); - ExitOnFailure(hr, "Failed to allocate memory for the temp path."); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); if (!::GetTempPathW(cchTempPath, sczTempPath)) { - ExitWithLastError(hr, "Failed to get temp path."); + PathExitWithLastError(hr, "Failed to get temp path."); } } @@ -514,10 +529,10 @@ DAPI_(HRESULT) PathCreateTempFile( for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i) { hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); - ExitOnFailure(hr, "Failed to allocate memory for file template."); + PathExitOnFailure(hr, "Failed to allocate memory for file template."); hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); - ExitOnFailure(hr, "Failed to allocate temp file name."); + PathExitOnFailure(hr, "Failed to allocate temp file name."); hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); if (INVALID_HANDLE_VALUE == hTempFile) @@ -528,7 +543,7 @@ DAPI_(HRESULT) PathCreateTempFile( { hr = S_OK; } - ExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); + PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); } } } @@ -538,17 +553,17 @@ DAPI_(HRESULT) PathCreateTempFile( if (INVALID_HANDLE_VALUE == hTempFile) { hr = StrAlloc(&sczTempFile, MAX_PATH); - ExitOnFailure(hr, "Failed to allocate memory for the temp path"); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) { - ExitWithLastError(hr, "Failed to create new temp file name."); + PathExitWithLastError(hr, "Failed to create new temp file name."); } hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); if (INVALID_HANDLE_VALUE == hTempFile) { - ExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); + PathExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); } } @@ -556,7 +571,7 @@ DAPI_(HRESULT) PathCreateTempFile( if (psczTempFile) { hr = StrAllocString(psczTempFile, sczTempFile, 0); - ExitOnFailure(hr, "Failed to copy temp file string."); + PathExitOnFailure(hr, "Failed to copy temp file string."); } if (phTempFile) @@ -602,24 +617,24 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile( if (wzDirectory && *wzDirectory) { hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix); - ExitOnFailure(hr, "Failed to combine directory and log prefix."); + PathExitOnFailure(hr, "Failed to combine directory and log prefix."); } else { if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) { - ExitWithLastError(hr, "Failed to get temp folder."); + PathExitWithLastError(hr, "Failed to get temp folder."); } hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); - ExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); + PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); } hr = PathGetDirectory(sczPrefix, &sczPrefixFolder); if (S_OK == hr) { hr = DirEnsureExists(sczPrefixFolder, NULL); - ExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); + PathExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); } if (!wzPostfix) @@ -636,7 +651,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile( // Log format: pre YYYY MM dd hh mm ss post ext 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); - ExitOnFailure(hr, "failed to allocate memory for the temp path"); + PathExitOnFailure(hr, "failed to allocate memory for the temp path"); hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hTempFile) @@ -655,14 +670,14 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile( } hr = HRESULT_FROM_WIN32(er); - ExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); + PathExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); } } while (fRetry); if (psczTempFile) { hr = StrAllocString(psczTempFile, sczTempPath, 0); - ExitOnFailure(hr, "Failed to copy temp path to return."); + PathExitOnFailure(hr, "Failed to copy temp path to return."); } if (phTempFile) @@ -701,29 +716,29 @@ DAPI_(HRESULT) PathCreateTempDirectory( if (wzDirectory && *wzDirectory) { hr = StrAllocString(&sczTempPath, wzDirectory, 0); - ExitOnFailure(hr, "Failed to copy temp path."); + PathExitOnFailure(hr, "Failed to copy temp path."); hr = PathBackslashTerminate(&sczTempPath); - ExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); + PathExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); } else { hr = StrAlloc(&sczTempPath, cchTempPath); - ExitOnFailure(hr, "Failed to allocate memory for the temp path."); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); if (!::GetTempPathW(cchTempPath, sczTempPath)) { - ExitWithLastError(hr, "Failed to get temp path."); + PathExitWithLastError(hr, "Failed to get temp path."); } } for (DWORD i = 1; i <= dwUniqueCount; ++i) { hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i); - ExitOnFailure(hr, "Failed to allocate memory for directory name template."); + PathExitOnFailure(hr, "Failed to allocate memory for directory name template."); hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz); - ExitOnFailure(hr, "Failed to allocate temp directory name."); + PathExitOnFailure(hr, "Failed to allocate temp directory name."); if (!::CreateDirectoryW(*psczTempDirectory, NULL)) { @@ -750,10 +765,10 @@ DAPI_(HRESULT) PathCreateTempDirectory( break; } } - ExitOnFailure(hr, "Failed to create temp directory."); + PathExitOnFailure(hr, "Failed to create temp directory."); hr = PathBackslashTerminate(psczTempDirectory); - ExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); + PathExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); LExit: ReleaseStr(scz); @@ -771,13 +786,13 @@ DAPI_(HRESULT) PathGetKnownFolder( HRESULT hr = S_OK; hr = StrAlloc(psczKnownFolder, MAX_PATH); - ExitOnFailure(hr, "Failed to allocate memory for known folder."); + PathExitOnFailure(hr, "Failed to allocate memory for known folder."); hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); - ExitOnFailure(hr, "Failed to get known folder path."); + PathExitOnFailure(hr, "Failed to get known folder path."); hr = PathBackslashTerminate(psczKnownFolder); - ExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); + PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); LExit: return hr; @@ -804,23 +819,23 @@ DAPI_(HRESULT) PathConcat( if (!wzPath2 || !*wzPath2) { hr = StrAllocString(psczCombined, wzPath1, 0); - ExitOnFailure(hr, "Failed to copy just path1 to output."); + PathExitOnFailure(hr, "Failed to copy just path1 to output."); } else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) { hr = StrAllocString(psczCombined, wzPath2, 0); - ExitOnFailure(hr, "Failed to copy just path2 to output."); + PathExitOnFailure(hr, "Failed to copy just path2 to output."); } else { hr = StrAllocString(psczCombined, wzPath1, 0); - ExitOnFailure(hr, "Failed to copy path1 to output."); + PathExitOnFailure(hr, "Failed to copy path1 to output."); hr = PathBackslashTerminate(psczCombined); - ExitOnFailure(hr, "Failed to backslashify."); + PathExitOnFailure(hr, "Failed to backslashify."); hr = StrAllocConcat(psczCombined, wzPath2, 0); - ExitOnFailure(hr, "Failed to append path2 to output."); + PathExitOnFailure(hr, "Failed to append path2 to output."); } LExit: @@ -839,13 +854,13 @@ DAPI_(HRESULT) PathEnsureQuoted( size_t cchPath = 0; hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath); - ExitOnFailure(hr, "Failed to get the length of the path."); + PathExitOnFailure(hr, "Failed to get the length of the path."); // Handle simple special cases. if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0])) { hr = StrAllocString(ppszPath, L"\"\"", 2); - ExitOnFailure(hr, "Failed to allocate a quoted empty string."); + PathExitOnFailure(hr, "Failed to allocate a quoted empty string."); ExitFunction(); } @@ -853,7 +868,7 @@ DAPI_(HRESULT) PathEnsureQuoted( if (L'"' != (*ppszPath)[0]) { hr = StrAllocPrefix(ppszPath, L"\"", 1); - ExitOnFailure(hr, "Failed to allocate an opening quote."); + PathExitOnFailure(hr, "Failed to allocate an opening quote."); // Add a char for the opening quote. ++cchPath; @@ -862,7 +877,7 @@ DAPI_(HRESULT) PathEnsureQuoted( if (L'"' != (*ppszPath)[cchPath - 1]) { hr = StrAllocConcat(ppszPath, L"\"", 1); - ExitOnFailure(hr, "Failed to allocate a closing quote."); + PathExitOnFailure(hr, "Failed to allocate a closing quote."); // Add a char for the closing quote. ++cchPath; @@ -876,7 +891,7 @@ DAPI_(HRESULT) PathEnsureQuoted( (*ppszPath)[cchPath - 1] = L'\\'; hr = StrAllocConcat(ppszPath, L"\"", 1); - ExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); + PathExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); } } @@ -897,10 +912,10 @@ DAPI_(HRESULT) PathCompare( LPWSTR sczPath2 = NULL; hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - ExitOnFailure(hr, "Failed to expand path1."); + PathExitOnFailure(hr, "Failed to expand path1."); hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - ExitOnFailure(hr, "Failed to expand path2."); + PathExitOnFailure(hr, "Failed to expand path2."); *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); @@ -922,7 +937,7 @@ DAPI_(HRESULT) PathCompress( hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (INVALID_HANDLE_VALUE == hPath) { - ExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); + PathExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); } DWORD dwBytesReturned = 0; @@ -933,7 +948,7 @@ DAPI_(HRESULT) PathCompress( DWORD er = ::GetLastError(); if (ERROR_INVALID_FUNCTION != er) { - ExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); + PathExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); } } @@ -945,7 +960,7 @@ LExit: DAPI_(HRESULT) PathGetHierarchyArray( __in_z LPCWSTR wzPath, - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczPathArray, + __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, __inout LPUINT pcPathArray ) { @@ -975,16 +990,16 @@ DAPI_(HRESULT) PathGetHierarchyArray( Assert(cArraySpacesNeeded >= 1); hr = MemEnsureArraySize(reinterpret_cast(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); - ExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); + PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); *pcPathArray = cArraySpacesNeeded; hr = StrAllocString(&sczPathCopy, wzPath, 0); - ExitOnFailure(hr, "Failed to allocate copy of original path"); + PathExitOnFailure(hr, "Failed to allocate copy of original path"); for (DWORD i = 0; i < cArraySpacesNeeded; ++i) { hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); - ExitOnFailure(hr, "Failed to copy path"); + PathExitOnFailure(hr, "Failed to copy path"); // 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 if (wzPath[lstrlenW(sczPathCopy) - 1] == L'\\') @@ -993,7 +1008,7 @@ DAPI_(HRESULT) PathGetHierarchyArray( } hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); - ExitOnFailure(hr, "Failed to get directory portion of path"); + PathExitOnFailure(hr, "Failed to get directory portion of path"); ReleaseStr(sczPathCopy); sczPathCopy = sczNewPathCopy; diff --git a/src/dutil/perfutil.cpp b/src/dutil/perfutil.cpp index 5c4e0774..bc138d34 100644 --- a/src/dutil/perfutil.cpp +++ b/src/dutil/perfutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define PerfExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) +#define PerfExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) +#define PerfExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) +#define PerfExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) +#define PerfExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PERFUTIL, e, x, s, __VA_ARGS__) +#define PerfExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PERFUTIL, g, x, s, __VA_ARGS__) + static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter static double vdFrequency = 1; diff --git a/src/dutil/polcutil.cpp b/src/dutil/polcutil.cpp index 1cc29e61..1fdfa18c 100644 --- a/src/dutil/polcutil.cpp +++ b/src/dutil/polcutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define PolcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) +#define PolcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) +#define PolcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) +#define PolcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) +#define PolcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_POLCUTIL, e, x, s, __VA_ARGS__) +#define PolcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_POLCUTIL, g, x, s, __VA_ARGS__) + const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\"; static HRESULT OpenPolicyKey( @@ -25,14 +40,14 @@ extern "C" HRESULT DAPI PolcReadNumber( { ExitFunction1(hr = S_FALSE); } - ExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); hr = RegReadNumber(hk, wzPolicyName, pdw); if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) { ExitFunction1(hr = S_FALSE); } - ExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); LExit: ReleaseRegKey(hk); @@ -60,14 +75,14 @@ extern "C" HRESULT DAPI PolcReadString( { ExitFunction1(hr = S_FALSE); } - ExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); hr = RegReadString(hk, wzPolicyName, pscz); if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) { ExitFunction1(hr = S_FALSE); } - ExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); LExit: ReleaseRegKey(hk); @@ -99,10 +114,10 @@ static HRESULT OpenPolicyKey( LPWSTR sczPath = NULL; hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath); - ExitOnFailure(hr, "Failed to combine logging path with root path."); + PolcExitOnFailure(hr, "Failed to combine logging path with root path."); hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk); - ExitOnFailure(hr, "Failed to open policy registry key."); + PolcExitOnFailure(hr, "Failed to open policy registry key."); LExit: ReleaseStr(sczPath); diff --git a/src/dutil/proc2utl.cpp b/src/dutil/proc2utl.cpp index 8a2fd09b..a59d2ffc 100644 --- a/src/dutil/proc2utl.cpp +++ b/src/dutil/proc2utl.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + /******************************************************************** ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable. @@ -21,7 +36,7 @@ extern "C" HRESULT DAPI ProcFindAllIdsFromExeName( hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hSnap) { - ExitWithLastError(hr, "Failed to create snapshot of processes on system"); + ProcExitWithLastError(hr, "Failed to create snapshot of processes on system"); } fContinue = ::Process32FirstW(hSnap, &peData); @@ -33,13 +48,13 @@ extern "C" HRESULT DAPI ProcFindAllIdsFromExeName( if (!*ppdwProcessIds) { *ppdwProcessIds = static_cast(MemAlloc(sizeof(DWORD), TRUE)); - ExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs."); + ProcExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs."); } else { DWORD* pdwReAllocReturnedPids = NULL; pdwReAllocReturnedPids = static_cast(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE)); - ExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs."); + ProcExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs."); *ppdwProcessIds = pdwReAllocReturnedPids; } diff --git a/src/dutil/proc3utl.cpp b/src/dutil/proc3utl.cpp index 038f002b..6d3cbc67 100644 --- a/src/dutil/proc3utl.cpp +++ b/src/dutil/proc3utl.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + static HRESULT GetActiveSessionUserToken( __out HANDLE *phToken ); @@ -25,20 +40,20 @@ extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser( PROCESS_INFORMATION pi = { }; hr = GetActiveSessionUserToken(&hToken); - ExitOnFailure(hr, "Failed to get active session user token."); + ProcExitOnFailure(hr, "Failed to get active session user token."); if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE)) { - ExitWithLastError(hr, "Failed to create environment block for UI process."); + ProcExitWithLastError(hr, "Failed to create environment block for UI process."); } hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine); - ExitOnFailure(hr, "Failed to allocate full command-line."); + ProcExitOnFailure(hr, "Failed to allocate full command-line."); si.cb = sizeof(si); if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi)) { - ExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine); + ProcExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine); } *phProcess = pi.hProcess; @@ -74,7 +89,7 @@ static HRESULT GetActiveSessionUserToken( // Loop through the sessions looking for the active one. if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions)) { - ExitWithLastError(hr, "Failed to enumerate sessions."); + ProcExitWithLastError(hr, "Failed to enumerate sessions."); } for (DWORD i = 0; i < cSessions; ++i) @@ -96,7 +111,7 @@ static HRESULT GetActiveSessionUserToken( // Get the user token from the active session. if (!::WTSQueryUserToken(dwSessionId, &hToken)) { - ExitWithLastError(hr, "Failed to get active session user token."); + ProcExitWithLastError(hr, "Failed to get active session user token."); } *phToken = hToken; diff --git a/src/dutil/procutil.cpp b/src/dutil/procutil.cpp index 9833d0ec..6bfe5017 100644 --- a/src/dutil/procutil.cpp +++ b/src/dutil/procutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + + // private functions static HRESULT CreatePipes( __out HANDLE *phOutRead, @@ -30,7 +45,7 @@ extern "C" HRESULT DAPI ProcElevated( if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) { - ExitWithLastError(hr, "Failed to open process token."); + ProcExitWithLastError(hr, "Failed to open process token."); } if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) @@ -50,7 +65,7 @@ extern "C" HRESULT DAPI ProcElevated( } else { - ExitOnRootFailure(hr, "Failed to get elevation token from process."); + ProcExitOnRootFailure(hr, "Failed to get elevation token from process."); } } @@ -76,7 +91,7 @@ extern "C" HRESULT DAPI ProcWow64( USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN; if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr)) { - ExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2."); + ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2."); } if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN) @@ -93,7 +108,7 @@ extern "C" HRESULT DAPI ProcWow64( { if (!pfnIsWow64Process(hProcess, &fIsWow64)) { - ExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process."); + ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process."); } } } @@ -121,7 +136,7 @@ extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection( if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState)) { - ExitWithLastError(hr, "Failed to disable file system redirection."); + ProcExitWithLastError(hr, "Failed to disable file system redirection."); } pfsr->fDisabled = TRUE; @@ -143,7 +158,7 @@ extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection( if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState)) { - ExitWithLastError(hr, "Failed to revert file system redirection."); + ProcExitWithLastError(hr, "Failed to revert file system redirection."); } pfsr->fDisabled = FALSE; @@ -168,13 +183,13 @@ extern "C" HRESULT DAPI ProcExec( PROCESS_INFORMATION pi = { }; hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L""); - ExitOnFailure(hr, "Failed to allocate full command-line."); + ProcExitOnFailure(hr, "Failed to allocate full command-line."); si.cb = sizeof(si); si.wShowWindow = static_cast(nCmdShow); if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) { - ExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine); + ProcExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine); } *phProcess = pi.hProcess; @@ -213,7 +228,7 @@ extern "C" HRESULT DAPI ProcExecute( // Create redirect pipes. hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); - ExitOnFailure(hr, "failed to create output pipes"); + ProcExitOnFailure(hr, "failed to create output pipes"); // Set up startup structure. si.cb = sizeof(STARTUPINFOW); @@ -249,7 +264,7 @@ extern "C" HRESULT DAPI ProcExecute( } else { - ExitWithLastError(hr, "Process failed to execute."); + ProcExitWithLastError(hr, "Process failed to execute."); } *phProcess = pi.hProcess; @@ -305,7 +320,7 @@ extern "C" HRESULT DAPI ProcWaitForCompletion( er = ::WaitForSingleObject(hProcess, dwTimeout); if (WAIT_FAILED == er) { - ExitWithLastError(hr, "Failed to wait for process to complete."); + ProcExitWithLastError(hr, "Failed to wait for process to complete."); } else if (WAIT_TIMEOUT == er) { @@ -314,7 +329,7 @@ extern "C" HRESULT DAPI ProcWaitForCompletion( if (!::GetExitCodeProcess(hProcess, &er)) { - ExitWithLastError(hr, "Failed to get process return code."); + ProcExitWithLastError(hr, "Failed to get process return code."); } *pReturnCode = er; @@ -340,7 +355,7 @@ extern "C" HRESULT DAPI ProcWaitForIds( DWORD cProcesses = 0; rghProcesses = static_cast(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); - ExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); + ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); for (DWORD i = 0; i < cProcessIds; ++i) { @@ -354,11 +369,11 @@ extern "C" HRESULT DAPI ProcWaitForIds( er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); if (WAIT_FAILED == er) { - ExitWithLastError(hr, "Failed to wait for process to complete."); + ProcExitWithLastError(hr, "Failed to wait for process to complete."); } else if (WAIT_TIMEOUT == er) { - ExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); + ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); } LExit: @@ -393,7 +408,7 @@ extern "C" HRESULT DAPI ProcCloseIds( { if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i])) { - ExitWithLastError(hr, "Failed to enumerate windows."); + ProcExitWithLastError(hr, "Failed to enumerate windows."); } } @@ -430,30 +445,30 @@ static HRESULT CreatePipes( // Create pipes if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) { - ExitWithLastError(hr, "failed to create output pipe"); + ProcExitWithLastError(hr, "failed to create output pipe"); } if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) { - ExitWithLastError(hr, "failed to create input pipe"); + ProcExitWithLastError(hr, "failed to create input pipe"); } // Duplicate output pipe so standard error and standard output write to the same pipe. if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - ExitWithLastError(hr, "failed to duplicate write handle"); + ProcExitWithLastError(hr, "failed to duplicate write handle"); } // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in // the child process that can't be closed. if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - ExitWithLastError(hr, "failed to duplicate output pipe"); + ProcExitWithLastError(hr, "failed to duplicate output pipe"); } if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - ExitWithLastError(hr, "failed to duplicate input pipe"); + ProcExitWithLastError(hr, "failed to duplicate input pipe"); } // now that everything has succeeded, assign to the outputs diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp index 1b13af81..e1ef19e8 100644 --- a/src/dutil/regutil.cpp +++ b/src/dutil/regutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define RegExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) +#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) +#define RegExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) +#define RegExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) +#define RegExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REGUTIL, e, x, s, __VA_ARGS__) +#define RegExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REGUTIL, g, x, s, __VA_ARGS__) + static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW; static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW; static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL; @@ -34,7 +49,7 @@ extern "C" HRESULT DAPI RegInitialize( HRESULT hr = S_OK; hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); - ExitOnFailure(hr, "Failed to load AdvApi32.dll"); + RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); @@ -114,7 +129,7 @@ extern "C" HRESULT DAPI RegCreate( DWORD er = ERROR_SUCCESS; er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); - ExitOnWin32Error(er, hr, "Failed to create registry key."); + RegExitOnWin32Error(er, hr, "Failed to create registry key."); LExit: return hr; @@ -140,7 +155,7 @@ HRESULT DAPI RegCreateEx( DWORD dwDisposition; er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition); - ExitOnWin32Error(er, hr, "Failed to create registry key."); + RegExitOnWin32Error(er, hr, "Failed to create registry key."); if (pfCreated) { @@ -171,7 +186,7 @@ extern "C" HRESULT DAPI RegOpen( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to open registry key."); + RegExitOnWin32Error(er, hr, "Failed to open registry key."); LExit: return hr; @@ -199,7 +214,7 @@ extern "C" HRESULT DAPI RegDelete( if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) { hr = E_INVALIDARG; - ExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); + RegExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); } switch (kbKeyBitness) @@ -222,18 +237,18 @@ extern "C" HRESULT DAPI RegDelete( { ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed to open this key for enumerating subkeys", wzSubKey); + RegExitOnFailure(hr, "Failed to open this key for enumerating subkeys: %ls", wzSubKey); // Yes, keep enumerating the 0th item, because we're deleting it every time while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) { - ExitOnFailure(hr, "Failed to enumerate key 0"); + RegExitOnFailure(hr, "Failed to enumerate key 0"); hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); - ExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); + RegExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); - ExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); + RegExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); } hr = S_OK; @@ -246,7 +261,7 @@ extern "C" HRESULT DAPI RegDelete( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); + RegExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); } else { @@ -255,7 +270,7 @@ extern "C" HRESULT DAPI RegDelete( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to delete registry key."); + RegExitOnWin32Error(er, hr, "Failed to delete registry key."); } LExit: @@ -284,7 +299,7 @@ extern "C" HRESULT DAPI RegKeyEnum( if (psczKey && *psczKey) { hr = StrMaxLength(*psczKey, reinterpret_cast(&cch)); - ExitOnFailure(hr, "Failed to determine length of string."); + RegExitOnFailure(hr, "Failed to determine length of string."); } if (2 > cch) @@ -292,18 +307,18 @@ extern "C" HRESULT DAPI RegKeyEnum( cch = 2; hr = StrAlloc(psczKey, cch); - ExitOnFailure(hr, "Failed to allocate string to minimum size."); + RegExitOnFailure(hr, "Failed to allocate string to minimum size."); } er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); if (ERROR_MORE_DATA == er) { er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL); - ExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); + RegExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator. hr = StrAlloc(psczKey, cch); - ExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); + RegExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); } @@ -311,7 +326,7 @@ extern "C" HRESULT DAPI RegKeyEnum( { ExitFunction1(hr = E_NOMOREITEMS); } - ExitOnWin32Error(er, hr, "Failed to enum registry key."); + RegExitOnWin32Error(er, hr, "Failed to enum registry key."); // Always ensure the registry key name is null terminated. #pragma prefast(push) @@ -340,20 +355,20 @@ HRESULT DAPI RegValueEnum( DWORD cbValueName = 0; er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL); - ExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); + RegExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); // Add one for null terminator ++cbValueName; hr = StrAlloc(psczName, cbValueName); - ExitOnFailure(hr, "Failed to allocate array for registry value name"); + RegExitOnFailure(hr, "Failed to allocate array for registry value name"); er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL); if (ERROR_NO_MORE_ITEMS == er) { ExitFunction1(hr = E_NOMOREITEMS); } - ExitOnWin32Error(er, hr, "Failed to enumerate registry value"); + RegExitOnWin32Error(er, hr, "Failed to enumerate registry value"); LExit: return hr; @@ -376,7 +391,7 @@ HRESULT DAPI RegGetType( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to read registry value."); + RegExitOnWin32Error(er, hr, "Failed to read registry value."); LExit: return hr; @@ -400,20 +415,20 @@ HRESULT DAPI RegReadBinary( DWORD dwType = 0; er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); - ExitOnWin32Error(er, hr, "Failed to get size of registry value."); + RegExitOnWin32Error(er, hr, "Failed to get size of registry value."); // Zero-length binary values can exist if (0 < cb) { pbBuffer = static_cast(MemAlloc(cb, FALSE)); - ExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); + RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to read registry value."); + RegExitOnWin32Error(er, hr, "Failed to read registry value."); } if (REG_BINARY == dwType) @@ -425,7 +440,7 @@ HRESULT DAPI RegReadBinary( else { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - ExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); + RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); } LExit: @@ -455,7 +470,7 @@ extern "C" HRESULT DAPI RegReadString( if (psczValue && *psczValue) { hr = StrMaxLength(*psczValue, reinterpret_cast(&cch)); - ExitOnFailure(hr, "Failed to determine length of string."); + RegExitOnFailure(hr, "Failed to determine length of string."); } if (2 > cch) @@ -463,7 +478,7 @@ extern "C" HRESULT DAPI RegReadString( cch = 2; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string to minimum size."); + RegExitOnFailure(hr, "Failed to allocate string to minimum size."); } cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. @@ -472,7 +487,7 @@ extern "C" HRESULT DAPI RegReadString( { cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string bigger for registry value."); + RegExitOnFailure(hr, "Failed to allocate string bigger for registry value."); er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); } @@ -480,7 +495,7 @@ extern "C" HRESULT DAPI RegReadString( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to read registry key."); + RegExitOnWin32Error(er, hr, "Failed to read registry key."); if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) { @@ -490,16 +505,16 @@ extern "C" HRESULT DAPI RegReadString( if (REG_EXPAND_SZ == dwType) { hr = StrAllocString(&sczExpand, *psczValue, 0); - ExitOnFailure(hr, "Failed to copy registry value to expand."); + RegExitOnFailure(hr, "Failed to copy registry value to expand."); hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); - ExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); + RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); } } else { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - ExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); + RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); } LExit: @@ -516,7 +531,7 @@ LExit: HRESULT DAPI RegReadStringArray( __in HKEY hk, __in_z_opt LPCWSTR wzName, - __deref_out_ecount_opt(pcStrings) LPWSTR** prgsczStrings, + __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, __out DWORD *pcStrings ) { @@ -534,7 +549,7 @@ HRESULT DAPI RegReadStringArray( { cch = cb / sizeof(WCHAR); hr = StrAlloc(&sczValue, cch); - ExitOnFailure(hr, "Failed to allocate string for registry value."); + RegExitOnFailure(hr, "Failed to allocate string for registry value."); er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); } @@ -542,18 +557,18 @@ HRESULT DAPI RegReadStringArray( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to read registry key."); + RegExitOnWin32Error(er, hr, "Failed to read registry key."); if (cb / sizeof(WCHAR) != cch) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); + RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); } if (REG_MULTI_SZ != dwType) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - ExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); + RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); } // Value exists, but is empty, so no strings to return. @@ -568,7 +583,7 @@ HRESULT DAPI RegReadStringArray( if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); + RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); } cch = cb / sizeof(WCHAR); @@ -583,7 +598,7 @@ HRESULT DAPI RegReadStringArray( // There's one string for every null character encountered (except the extra 1 at the end of the string) *pcStrings = dwNullCharacters - 1; hr = MemEnsureArraySize(reinterpret_cast(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); - ExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); + RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); #pragma prefast(push) #pragma prefast(disable:26010) @@ -591,7 +606,7 @@ HRESULT DAPI RegReadStringArray( for (DWORD i = 0; i < *pcStrings; ++i) { hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); - ExitOnFailure(hr, "Failed to allocate copy of string"); + RegExitOnFailure(hr, "Failed to allocate copy of string"); // Skip past this string wzSource += lstrlenW(wzSource) + 1; @@ -630,19 +645,19 @@ extern "C" HRESULT DAPI RegReadVersion( if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) { hr = RegReadString(hk, wzName, &sczVersion); - ExitOnFailure(hr, "Failed to read registry version as string."); + RegExitOnFailure(hr, "Failed to read registry version as string."); hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); - ExitOnFailure(hr, "Failed to convert registry string to version."); + RegExitOnFailure(hr, "Failed to convert registry string to version."); } else if (REG_QWORD == dwType) { - ExitOnWin32Error(er, hr, "Failed to read registry key."); + RegExitOnWin32Error(er, hr, "Failed to read registry key."); } else // unexpected data type { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); } LExit: @@ -672,12 +687,12 @@ extern "C" HRESULT DAPI RegReadNumber( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to query registry key value."); + RegExitOnWin32Error(er, hr, "Failed to query registry key value."); if (REG_DWORD != dwType) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); } LExit: @@ -705,12 +720,12 @@ extern "C" HRESULT DAPI RegReadQword( { ExitFunction1(hr = E_FILENOTFOUND); } - ExitOnWin32Error(er, hr, "Failed to query registry key value."); + RegExitOnWin32Error(er, hr, "Failed to query registry key value."); if (REG_QWORD != dwType) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); } LExit: @@ -733,7 +748,7 @@ HRESULT DAPI RegWriteBinary( DWORD er = ERROR_SUCCESS; er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer); - ExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); + RegExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); LExit: return hr; @@ -788,7 +803,7 @@ extern "C" HRESULT DAPI RegWriteStringFormatted( va_start(args, szFormat); hr = StrAllocFormattedArgs(&sczValue, szFormat, args); va_end(args); - ExitOnFailure(hr, "Failed to allocate %ls value.", wzName); + RegExitOnFailure(hr, "Failed to allocate %ls value.", wzName); hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ); @@ -832,18 +847,18 @@ HRESULT DAPI RegWriteStringArray( { dwTemp = dwTotalStringSize; hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize); - ExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); + RegExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); } hr = StrAlloc(&sczWriteValue, dwTotalStringSize); - ExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); + RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); wzCopyDestination = sczWriteValue; dwTemp = dwTotalStringSize; for (DWORD i = 0; i < cValues; ++i) { hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); - ExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); + RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); dwTemp -= lstrlenW(rgwzValues[i]) + 1; wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; @@ -853,10 +868,10 @@ HRESULT DAPI RegWriteStringArray( } hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); - ExitOnFailure(hr, "Failed to get total string size in bytes"); + RegExitOnFailure(hr, "Failed to get total string size in bytes"); er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast(wzWriteValue), cbTotalStringSize); - ExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); + RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); LExit: ReleaseStr(sczWriteValue); @@ -878,7 +893,7 @@ extern "C" HRESULT DAPI RegWriteNumber( DWORD er = ERROR_SUCCESS; er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof(dwValue)); - ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); LExit: return hr; @@ -898,7 +913,7 @@ extern "C" HRESULT DAPI RegWriteQword( DWORD er = ERROR_SUCCESS; er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast(&qwValue), sizeof(qwValue)); - ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); LExit: return hr; @@ -918,7 +933,7 @@ extern "C" HRESULT DAPI RegQueryKey( DWORD er = ERROR_SUCCESS; er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL); - ExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); + RegExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); LExit: return hr; @@ -938,11 +953,11 @@ static HRESULT WriteStringToRegistry( if (wzValue) { - hr = ::StringCbLengthW(wzValue, DWORD_MAX, reinterpret_cast(&cbValue)); - ExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); + hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), reinterpret_cast(&cbValue)); + RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), cbValue); - ExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); + RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); } else { @@ -951,7 +966,7 @@ static HRESULT WriteStringToRegistry( { er = ERROR_SUCCESS; } - ExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); + RegExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); } LExit: diff --git a/src/dutil/resrutil.cpp b/src/dutil/resrutil.cpp index 1da03ed9..a6a7ee23 100644 --- a/src/dutil/resrutil.cpp +++ b/src/dutil/resrutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define ResrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) +#define ResrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) +#define ResrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) +#define ResrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) +#define ResrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESRUTIL, e, x, s, __VA_ARGS__) +#define ResrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESRUTIL, g, x, s, __VA_ARGS__) + #define RES_STRINGS_PER_BLOCK 16 @@ -33,7 +48,7 @@ extern "C" HRESULT DAPI ResGetStringLangId( if (wzPath && *wzPath) { hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); - ExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath); + ResrExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath); } #pragma prefast(push) @@ -41,7 +56,7 @@ extern "C" HRESULT DAPI ResGetStringLangId( if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast(EnumLangIdProc), reinterpret_cast(&wFoundLangId))) #pragma prefast(pop) { - ExitWithLastError(hr, "Failed to find string language identifier."); + ResrExitWithLastError(hr, "Failed to find string language identifier."); } *pwLangId = wFoundLangId; @@ -76,12 +91,12 @@ extern "C" HRESULT DAPI ResReadString( do { hr = StrAlloc(ppwzString, cch); - ExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch); if (0 == cchReturned) { - ExitWithLastError(hr, "Failed to load string resource id: %d", uID); + ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); } // if the returned string count is one character too small, it's likely we have @@ -92,7 +107,7 @@ extern "C" HRESULT DAPI ResReadString( hr = S_FALSE; } } while (S_FALSE == hr); - ExitOnFailure(hr, "Failed to load string resource id: %d", uID); + ResrExitOnFailure(hr, "Failed to load string resource id: %d", uID); LExit: return hr; @@ -119,7 +134,7 @@ extern "C" HRESULT DAPI ResReadStringAnsi( do { hr = StrAnsiAlloc(ppszString, cch); - ExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); #pragma prefast(push) #pragma prefast(disable:25068) @@ -127,7 +142,7 @@ extern "C" HRESULT DAPI ResReadStringAnsi( #pragma prefast(pop) if (0 == cchReturned) { - ExitWithLastError(hr, "Failed to load string resource id: %d", uID); + ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); } // if the returned string count is one character too small, it's likely we have @@ -138,7 +153,7 @@ extern "C" HRESULT DAPI ResReadStringAnsi( hr = S_FALSE; } } while (S_FALSE == hr); - ExitOnFailure(hr, "failed to load string resource id: %d", uID); + ResrExitOnFailure(hr, "failed to load string resource id: %d", uID); LExit: return hr; @@ -169,19 +184,19 @@ extern "C" HRESULT DAPI ResReadData( #pragma prefast(disable:25068) hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); #pragma prefast(pop) - ExitOnNullWithLastError(hRsrc, hr, "Failed to find resource."); + ResrExitOnNullWithLastError(hRsrc, hr, "Failed to find resource."); hData = ::LoadResource(hinst, hRsrc); - ExitOnNullWithLastError(hData, hr, "Failed to load resource."); + ResrExitOnNullWithLastError(hData, hr, "Failed to load resource."); cbData = ::SizeofResource(hinst, hRsrc); if (!cbData) { - ExitWithLastError(hr, "Failed to get size of resource."); + ResrExitWithLastError(hr, "Failed to get size of resource."); } *ppv = ::LockResource(hData); - ExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource."); + ResrExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource."); *pcb = cbData; LExit: @@ -207,18 +222,18 @@ extern "C" HRESULT DAPI ResExportDataToFile( BOOL bCreatedFile = FALSE; hr = ResReadData(NULL, szDataName, &pData, &cbData); - ExitOnFailure(hr, "Failed to GetData from %s.", szDataName); + ResrExitOnFailure(hr, "Failed to GetData from %s.", szDataName); hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { - ExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile); + ResrExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile); } bCreatedFile = TRUE; if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL)) { - ExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile); + ResrExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile); } LExit: diff --git a/src/dutil/reswutil.cpp b/src/dutil/reswutil.cpp index e534fc09..42b49c55 100644 --- a/src/dutil/reswutil.cpp +++ b/src/dutil/reswutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define ReswExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) +#define ReswExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) +#define ReswExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) +#define ReswExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) +#define ReswExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESWUTIL, e, x, s, __VA_ARGS__) +#define ReswExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESWUTIL, g, x, s, __VA_ARGS__) + #define RES_STRINGS_PER_BLOCK 16 // Internal data structure format for a string block in a resource table. @@ -66,31 +81,31 @@ extern "C" HRESULT DAPI ResWriteString( DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK); hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); - ExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); + ReswExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock); - ExitOnFailure(hr, "Failed to get string block to update."); + ReswExitOnFailure(hr, "Failed to get string block to update."); hr = StringBlockChangeString(&StrBlock, dwStringId, wzData); - ExitOnFailure(hr, "Failed to update string block string."); + ReswExitOnFailure(hr, "Failed to update string block string."); hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData); - ExitOnFailure(hr, "Failed to convert string block to resource data."); + ReswExitOnFailure(hr, "Failed to convert string block to resource data."); ::FreeLibrary(hModule); hModule = NULL; hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); - ExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData)) { - ExitWithLastError(hr, "Failed to ::UpdateResourceA."); + ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); } if (!::EndUpdateResource(hUpdate, FALSE)) { - ExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); } hUpdate = NULL; @@ -134,16 +149,16 @@ extern "C" HRESULT DAPI ResWriteData( HANDLE hUpdate = NULL; hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); - ExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData)) { - ExitWithLastError(hr, "Failed to ::UpdateResourceA."); + ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); } if (!::EndUpdateResource(hUpdate, FALSE)) { - ExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); } hUpdate = NULL; @@ -177,23 +192,23 @@ extern "C" HRESULT DAPI ResImportDataFromFile( hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { - ExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); + ReswExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); } cbFile = ::GetFileSize(hFile, NULL); if (!cbFile) { - ExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); + ReswExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); } hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - ExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); + ReswExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile); - ExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); + ReswExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile); - ExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); + ReswExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); LExit: if (pv) @@ -226,25 +241,25 @@ static HRESULT StringBlockInitialize( DWORD cbData = 0; hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId); - ExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); + ReswExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); hData = ::LoadResource(hModule, hRsrc); - ExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); + ReswExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); cbData = ::SizeofResource(hModule, hRsrc); if (!cbData) { - ExitWithLastError(hr, "Failed to ::SizeofResource."); + ReswExitWithLastError(hr, "Failed to ::SizeofResource."); } pvData = ::LockResource(hData); - ExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); + ReswExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); pStrBlock->dwBlockId = dwBlockId; pStrBlock->wLangId = wLangId; hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData); - ExitOnFailure(hr, "Failed to convert string block from resource data."); + ReswExitOnFailure(hr, "Failed to convert string block from resource data."); LExit: return hr; @@ -276,10 +291,10 @@ static HRESULT StringBlockChangeString( DWORD cchData = lstrlenW(szData); pwzData = static_cast(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); - ExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); + ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); hr = ::StringCchCopyW(pwzData, cchData + 1, szData); - ExitOnFailure(hr, "Failed to copy new block string."); + ReswExitOnFailure(hr, "Failed to copy new block string."); ReleaseNullMem(pStrBlock->rgwz[dwStringId]); @@ -311,7 +326,7 @@ static HRESULT StringBlockConvertToResourceData( cbData *= sizeof(WCHAR); pvData = MemAlloc(cbData, TRUE); - ExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); + ReswExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); pwz = static_cast(pvData); for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) @@ -355,10 +370,10 @@ static HRESULT StringBlockConvertFromResourceData( ++pwzParse; pStrBlock->rgwz[i] = static_cast(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE)); - ExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); + ReswExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ExitOnFailure(hr, "Failed to copy parsed resource data into string block."); + ReswExitOnFailure(hr, "Failed to copy parsed resource data into string block."); pwzParse += cchParse; } diff --git a/src/dutil/rexutil.cpp b/src/dutil/rexutil.cpp index 73500630..155ca714 100644 --- a/src/dutil/rexutil.cpp +++ b/src/dutil/rexutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" #include "rexutil.h" + +// Exit macros +#define RexExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) +#define RexExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) +#define RexExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) +#define RexExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) +#define RexExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REXUTIL, e, x, s, __VA_ARGS__) +#define RexExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REXUTIL, g, x, s, __VA_ARGS__) + // // static globals // @@ -60,7 +75,7 @@ extern "C" HRESULT RexInitialize() if (!vhfdi) { hr = E_FAIL; - ExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here + RexExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here } ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable)); @@ -123,12 +138,12 @@ extern "C" HRESULT RexExtract( // load the cabinet resource // hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); - ExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); + RexExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10)); //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info"); hRes = ::LoadResource(NULL, hResInfo); - ExitOnNullWithLastError(hRes, hr, "failed to load resource"); + RexExitOnNullWithLastError(hRes, hr, "failed to load resource"); vcbRes = ::SizeofResource(NULL, hResInfo); vpbRes = (const BYTE*)::LockResource(hRes); @@ -140,11 +155,11 @@ extern "C" HRESULT RexExtract( // //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL)) //{ - // ExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); + // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); //} hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); - ExitOnFailure(hr, "Failed to copy resource name to global."); + RexExitOnFailure(hr, "Failed to copy resource name to global."); // // iterate through files in cabinet extracting them to the callback function @@ -193,7 +208,7 @@ static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int o if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); + RexExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); } // find an empty spot in the fake file table @@ -209,7 +224,7 @@ static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int o if (FILETABLESIZE <= i) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "File table exceeded"); + RexExitOnFailure(hr, "File table exceeded"); } if (0 == lstrcmpA(vszResource, pszFile)) @@ -225,7 +240,7 @@ static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int o hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { - ExitWithLastError(hr, "failed to open file: %s", pszFile); + RexExitWithLastError(hr, "failed to open file: %s", pszFile); } vrgffFileTable[i].fUsed = TRUE; @@ -267,7 +282,7 @@ static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL)) { - ExitWithLastError(hr, "failed to read during cabinet extraction"); + RexExitWithLastError(hr, "failed to read during cabinet extraction"); } } @@ -292,7 +307,7 @@ static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); if (!::WriteFile(reinterpret_cast(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL)) { - ExitWithLastError(hr, "failed to write during cabinet extraction"); + RexExitWithLastError(hr, "failed to write during cabinet extraction"); } // call the writer callback if defined @@ -333,7 +348,7 @@ static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektyp default : dwMoveMethod = 0; hr = E_UNEXPECTED; - ExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + RexExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); } if (MEMORY_FILE == vrgffFileTable[hf].fftType) @@ -362,7 +377,7 @@ static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektyp lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod); if (0xFFFFFFFF == lMove) { - ExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + RexExitWithLastError(hr, "failed to move file pointer %d bytes", dist); } } @@ -394,7 +409,7 @@ __callback int FAR DIAMONDAPI RexClose(INT_PTR hf) if (!::CloseHandle(vrgffFileTable[hf].hFile)) { - ExitWithLastError(hr, "failed to close file during cabinet extraction"); + RexExitWithLastError(hr, "failed to close file during cabinet extraction"); } vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE; @@ -440,7 +455,7 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati sz = static_cast(pFDINotify->psz1); if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) { - ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); } if (prcs->pfnProgress) @@ -457,25 +472,25 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati // get the created date for the resource in the cabinet if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) { - ExitWithLastError(hr, "failed to get time for resource: %ls", wz); + RexExitWithLastError(hr, "failed to get time for resource: %ls", wz); } WCHAR wzPath[MAX_PATH]; hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); - ExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); + RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); if (L'*' == *prcs->pwzExtract) { hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); } else { Assert(*prcs->pwzExtractName); hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); - ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); } // Quickly chop off the file name part of the path to ensure the path exists @@ -486,7 +501,7 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati *wzFile = L'\0'; hr = DirEnsureExists(wzPath, NULL); - ExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); + RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); hr = S_OK; @@ -505,14 +520,14 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati if (FILETABLESIZE <= i) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "File table exceeded"); + RexExitOnFailure(hr, "File table exceeded"); } // open the file hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { - ExitWithLastError(hr, "failed to open file: %ls", wzPath); + RexExitWithLastError(hr, "failed to open file: %ls", wzPath); } vrgffFileTable[i].fUsed = TRUE; @@ -545,7 +560,7 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati sz = static_cast(pFDINotify->psz1); if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) { - ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); } RexClose(pFDINotify->hf); diff --git a/src/dutil/rmutil.cpp b/src/dutil/rmutil.cpp index 75d3e277..95c8c8a4 100644 --- a/src/dutil/rmutil.cpp +++ b/src/dutil/rmutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" #include + +// Exit macros +#define RmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) +#define RmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) +#define RmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) +#define RmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) +#define RmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RMUTIL, e, x, s, __VA_ARGS__) +#define RmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RMUTIL, g, x, s, __VA_ARGS__) + #define ARRAY_GROWTH_SIZE 5 typedef DWORD (WINAPI *PFNRMJOINSESSION)( @@ -80,13 +95,13 @@ extern "C" HRESULT DAPI RmuJoinSession( *ppSession = NULL; pSession = static_cast(MemAlloc(sizeof(RMU_SESSION), TRUE)); - ExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); + RmExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); hr = RmuInitialize(); - ExitOnFailure(hr, "Failed to initialize Restart Manager."); + RmExitOnFailure(hr, "Failed to initialize Restart Manager."); er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey); - ExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); + RmExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); ::InitializeCriticalSection(&pSession->cs); pSession->fInitialized = TRUE; @@ -120,7 +135,7 @@ extern "C" HRESULT DAPI RmuAddFile( // Create or grow the jagged array. hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0); - ExitOnFailure(hr, "Failed to add the filename to the array."); + RmExitOnFailure(hr, "Failed to add the filename to the array."); LExit: ::LeaveCriticalSection(&pSession->cs); @@ -161,29 +176,29 @@ extern "C" HRESULT DAPI RmuAddProcessById( // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context. if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) { - ExitWithLastError(hr, "Failed to get process token."); + RmExitWithLastError(hr, "Failed to get process token."); } priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) { - ExitWithLastError(hr, "Failed to get debug privilege LUID."); + RmExitWithLastError(hr, "Failed to get debug privilege LUID."); } cbPrevPriv = sizeof(TOKEN_PRIVILEGES); pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); - ExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); + RmExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) { LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); + RmExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); pPrevPriv = static_cast(pv); if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) { - ExitWithLastError(hr, "Failed to get debug privilege LUID."); + RmExitWithLastError(hr, "Failed to get debug privilege LUID."); } } @@ -195,13 +210,13 @@ extern "C" HRESULT DAPI RmuAddProcessById( { if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime)) { - ExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId); + RmExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId); } ::EnterCriticalSection(&pSession->cs); fLocked = TRUE; hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime); - ExitOnFailure(hr, "Failed to add the application to the array."); + RmExitOnFailure(hr, "Failed to add the application to the array."); } else { @@ -213,7 +228,7 @@ extern "C" HRESULT DAPI RmuAddProcessById( } else { - ExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId); + RmExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId); } } @@ -258,7 +273,7 @@ extern "C" HRESULT DAPI RmuAddProcessesByName( BOOL fNotFound = FALSE; hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds); - ExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName); + RmExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName); for (DWORD i = 0; i < cProcessIds; ++i) { @@ -270,7 +285,7 @@ extern "C" HRESULT DAPI RmuAddProcessesByName( } else { - ExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]); + RmExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]); } } @@ -303,7 +318,7 @@ extern "C" HRESULT DAPI RmuAddService( ::EnterCriticalSection(&pSession->cs); hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0); - ExitOnFailure(hr, "Failed to add the service name to the array."); + RmExitOnFailure(hr, "Failed to add the service name to the array."); LExit: ::LeaveCriticalSection(&pSession->cs); @@ -341,7 +356,7 @@ extern "C" HRESULT DAPI RmuRegisterResources( pSession->cServiceNames, pSession->rgsczServiceNames ); - ExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); + RmExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); // Empty the arrays if registered in case additional resources are added later. ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); @@ -373,11 +388,11 @@ extern "C" HRESULT DAPI RmuEndSession( if (!pSession->fStartedSessionHandle) { hr = RmuRegisterResources(pSession); - ExitOnFailure(hr, "Failed to register remaining resources."); + RmExitOnFailure(hr, "Failed to register remaining resources."); } er = vpfnRmEndSession(pSession->dwSessionHandle); - ExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); + RmExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); LExit: if (pSession->fInitialized) @@ -404,16 +419,16 @@ static HRESULT RmuInitialize() if (1 == iRef && !vhModule) { hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule); - ExitOnFailure(hr, "Failed to load the rstrtmgr.dll module."); + RmExitOnFailure(hr, "Failed to load the rstrtmgr.dll module."); vpfnRmJoinSession = reinterpret_cast(::GetProcAddress(hModule, "RmJoinSession")); - ExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll."); + RmExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll."); vpfnRmRegisterResources = reinterpret_cast(::GetProcAddress(hModule, "RmRegisterResources")); - ExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll."); + RmExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll."); vpfnRmEndSession = reinterpret_cast(::GetProcAddress(hModule, "RmEndSession")); - ExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll."); + RmExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll."); vhModule = hModule; } @@ -447,7 +462,7 @@ static HRESULT RmuApplicationArrayAlloc( RM_UNIQUE_PROCESS *pApplication = NULL; hr = MemEnsureArraySize(reinterpret_cast(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE); - ExitOnFailure(hr, "Failed to allocate memory for the application array."); + RmExitOnFailure(hr, "Failed to allocate memory for the application array."); pApplication = static_cast(&(*prgApplications)[*pcApplications]); pApplication->dwProcessId = dwProcessId; @@ -466,7 +481,7 @@ static HRESULT RmuApplicationArrayFree( HRESULT hr = S_OK; hr = MemFree(rgApplications); - ExitOnFailure(hr, "Failed to free memory for the application array."); + RmExitOnFailure(hr, "Failed to free memory for the application array."); LExit: return hr; diff --git a/src/dutil/rssutil.cpp b/src/dutil/rssutil.cpp index db49d954..8f994dfc 100644 --- a/src/dutil/rssutil.cpp +++ b/src/dutil/rssutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define RssExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) +#define RssExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) +#define RssExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) +#define RssExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) +#define RssExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RSSUTIL, e, x, s, __VA_ARGS__) +#define RssExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RSSUTIL, g, x, s, __VA_ARGS__) + static HRESULT ParseRssDocument( __in IXMLDOMDocument *pixd, __out RSS_CHANNEL **ppChannel @@ -68,10 +83,10 @@ extern "C" HRESULT DAPI RssParseFromString( IXMLDOMDocument *pixdRss = NULL; hr = XmlLoadDocument(wzRssString, &pixdRss); - ExitOnFailure(hr, "Failed to load RSS string as XML document."); + RssExitOnFailure(hr, "Failed to load RSS string as XML document."); hr = ParseRssDocument(pixdRss, &pNewChannel); - ExitOnFailure(hr, "Failed to parse RSS document."); + RssExitOnFailure(hr, "Failed to parse RSS document."); *ppChannel = pNewChannel; pNewChannel = NULL; @@ -102,10 +117,10 @@ extern "C" HRESULT DAPI RssParseFromFile( IXMLDOMDocument *pixdRss = NULL; hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss); - ExitOnFailure(hr, "Failed to load RSS string as XML document."); + RssExitOnFailure(hr, "Failed to load RSS string as XML document."); hr = ParseRssDocument(pixdRss, &pNewChannel); - ExitOnFailure(hr, "Failed to parse RSS document."); + RssExitOnFailure(hr, "Failed to parse RSS document."); *ppChannel = pNewChannel; pNewChannel = NULL; @@ -175,17 +190,17 @@ static HRESULT ParseRssDocument( // Get the document element and start processing channels. // hr = pixd ->get_documentElement(&pRssElement); - ExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); + RssExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); hr = pRssElement->get_childNodes(&pChannelNodes); - ExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); + RssExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName))) { if (0 == lstrcmpW(bstrNodeName, L"channel")) { hr = ParseRssChannel(pNode, &pNewChannel); - ExitOnFailure(hr, "Failed to parse RSS channel."); + RssExitOnFailure(hr, "Failed to parse RSS channel."); } else if (0 == lstrcmpW(bstrNodeName, L"link")) { @@ -242,13 +257,13 @@ static HRESULT ParseRssChannel( // the RSS_CHANNEL structure // hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList); - ExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); + RssExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); hr = pNodeList->get_length(&cItems); - ExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); + RssExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); pNewChannel = static_cast(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE)); - ExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); + RssExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); pNewChannel->cItems = cItems; @@ -256,7 +271,7 @@ static HRESULT ParseRssChannel( // Process the elements under a channel now. // hr = pixnChannel->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); + RssExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); cItems = 0; // reset the counter and use this to walk through the channel items while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) @@ -264,45 +279,45 @@ static HRESULT ParseRssChannel( if (0 == lstrcmpW(bstrNodeName, L"title")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS channel title."); + RssExitOnFailure(hr, "Failed to get RSS channel title."); hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS channel title."); + RssExitOnFailure(hr, "Failed to allocate RSS channel title."); } else if (0 == lstrcmpW(bstrNodeName, L"link")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS channel link."); + RssExitOnFailure(hr, "Failed to get RSS channel link."); hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS channel link."); + RssExitOnFailure(hr, "Failed to allocate RSS channel link."); } else if (0 == lstrcmpW(bstrNodeName, L"description")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS channel description."); + RssExitOnFailure(hr, "Failed to get RSS channel description."); hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS channel description."); + RssExitOnFailure(hr, "Failed to allocate RSS channel description."); } else if (0 == lstrcmpW(bstrNodeName, L"ttl")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS channel description."); + RssExitOnFailure(hr, "Failed to get RSS channel description."); pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10); } else if (0 == lstrcmpW(bstrNodeName, L"item")) { hr = ParseRssItem(pNode, cItems, pNewChannel); - ExitOnFailure(hr, "Failed to parse RSS item."); + RssExitOnFailure(hr, "Failed to parse RSS item."); ++cItems; } else { hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements); - ExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); + RssExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeValue); @@ -349,7 +364,7 @@ static HRESULT ParseRssItem( if (pChannel->cItems <= cItem) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected number of items parsed."); + RssExitOnFailure(hr, "Unexpected number of items parsed."); } pItem = pChannel->rgItems + cItem; @@ -358,71 +373,71 @@ static HRESULT ParseRssItem( // Process the elements under an item now. // hr = pixnItem->get_childNodes(&pNodeList); - ExitOnFailure(hr, "Failed to get child nodes of RSS item element."); + RssExitOnFailure(hr, "Failed to get child nodes of RSS item element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (0 == lstrcmpW(bstrNodeName, L"title")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS channel title."); + RssExitOnFailure(hr, "Failed to get RSS channel title."); hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS item title."); + RssExitOnFailure(hr, "Failed to allocate RSS item title."); } else if (0 == lstrcmpW(bstrNodeName, L"link")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS channel link."); + RssExitOnFailure(hr, "Failed to get RSS channel link."); hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS item link."); + RssExitOnFailure(hr, "Failed to allocate RSS item link."); } else if (0 == lstrcmpW(bstrNodeName, L"description")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS item description."); + RssExitOnFailure(hr, "Failed to get RSS item description."); hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS item description."); + RssExitOnFailure(hr, "Failed to allocate RSS item description."); } else if (0 == lstrcmpW(bstrNodeName, L"guid")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS item guid."); + RssExitOnFailure(hr, "Failed to get RSS item guid."); hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS item guid."); + RssExitOnFailure(hr, "Failed to allocate RSS item guid."); } else if (0 == lstrcmpW(bstrNodeName, L"pubDate")) { hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS item guid."); + RssExitOnFailure(hr, "Failed to get RSS item guid."); hr = TimeFromString(bstrNodeValue, &pItem->ftPublished); - ExitOnFailure(hr, "Failed to convert RSS item time."); + RssExitOnFailure(hr, "Failed to convert RSS item time."); } else if (0 == lstrcmpW(bstrNodeName, L"enclosure")) { hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS item enclosure url."); + RssExitOnFailure(hr, "Failed to get RSS item enclosure url."); hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); + RssExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); ReleaseNullBSTR(bstrNodeValue); hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize); - ExitOnFailure(hr, "Failed to get RSS item enclosure length."); + RssExitOnFailure(hr, "Failed to get RSS item enclosure length."); hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue); - ExitOnFailure(hr, "Failed to get RSS item enclosure type."); + RssExitOnFailure(hr, "Failed to get RSS item enclosure type."); hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); + RssExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); } else { hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements); - ExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); + RssExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeValue); @@ -460,39 +475,39 @@ static HRESULT ParseRssUnknownElement( RSS_UNKNOWN_ELEMENT* pNewUnknownElement; pNewUnknownElement = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE)); - ExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + RssExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); - ExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); } else if (S_FALSE == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get unknown element namespace."); + RssExitOnFailure(hr, "Failed to get unknown element namespace."); hr = pNode->get_baseName(&bstrNodeName); - ExitOnFailure(hr, "Failed to get unknown element name."); + RssExitOnFailure(hr, "Failed to get unknown element name."); hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); - ExitOnFailure(hr, "Failed to allocate RSS unknown element name."); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element name."); hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get unknown element value."); + RssExitOnFailure(hr, "Failed to get unknown element value."); hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS unknown element value."); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element value."); hr = pNode->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "Failed get attributes on RSS unknown element."); + RssExitOnFailure(hr, "Failed get attributes on RSS unknown element."); while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) { hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); - ExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); + RssExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); ReleaseNullObject(pixnAttribute); } @@ -501,7 +516,7 @@ static HRESULT ParseRssUnknownElement( { hr = S_OK; } - ExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); + RssExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; while (*ppTail) @@ -543,31 +558,31 @@ static HRESULT ParseRssUnknownAttribute( RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; pNewUnknownAttribute = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE)); - ExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + RssExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); - ExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); } else if (S_FALSE == hr) { hr = S_OK; } - ExitOnFailure(hr, "Failed to get unknown attribute namespace."); + RssExitOnFailure(hr, "Failed to get unknown attribute namespace."); hr = pNode->get_baseName(&bstrNodeName); - ExitOnFailure(hr, "Failed to get unknown attribute name."); + RssExitOnFailure(hr, "Failed to get unknown attribute name."); hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); - ExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); hr = XmlGetText(pNode, &bstrNodeValue); - ExitOnFailure(hr, "Failed to get unknown attribute value."); + RssExitOnFailure(hr, "Failed to get unknown attribute value."); hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); - ExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; while (*ppTail) diff --git a/src/dutil/shelutil.cpp b/src/dutil/shelutil.cpp index a69c9eaa..2eb9a52a 100644 --- a/src/dutil/shelutil.cpp +++ b/src/dutil/shelutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__) +#define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__) + static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; static HRESULT GetDesktopShellView( @@ -55,7 +70,7 @@ extern "C" HRESULT DAPI ShelExec( if (!vpfnShellExecuteExW(&shExecInfo)) { - ExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); + ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); } if (phProcess) @@ -93,44 +108,44 @@ extern "C" HRESULT DAPI ShelExecUnelevated( IShellDispatch2* psd = NULL; bstrTargetPath = ::SysAllocString(wzTargetPath); - ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); if (wzParameters && *wzParameters) { vtParameters.vt = VT_BSTR; vtParameters.bstrVal = ::SysAllocString(wzParameters); - ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); } if (wzVerb && *wzVerb) { vtVerb.vt = VT_BSTR; vtVerb.bstrVal = ::SysAllocString(wzVerb); - ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); } if (wzWorkingDirectory && *wzWorkingDirectory) { vtWorkingDirectory.vt = VT_BSTR; vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); - ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); } vtShow.vt = VT_INT; vtShow.intVal = nShowCmd; hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); - ExitOnFailure(hr, "Failed to get desktop shell view."); + ShelExitOnFailure(hr, "Failed to get desktop shell view."); hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); - ExitOnFailure(hr, "Failed to get shell dispatch from view."); + ShelExitOnFailure(hr, "Failed to get shell dispatch from view."); hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } - ExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); + ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); LExit: ReleaseObject(psd); @@ -157,13 +172,13 @@ extern "C" HRESULT DAPI ShelGetFolder( WCHAR wzPath[MAX_PATH]; hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); - ExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); + ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); hr = StrAllocString(psczFolderPath, wzPath, 0); - ExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); hr = PathBackslashTerminate(psczFolderPath); - ExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); LExit: return hr; @@ -206,19 +221,19 @@ extern "C" HRESULT DAPI ShelGetKnownFolder( TraceError(hr, "Failed to load shell32.dll"); ExitFunction1(hr = E_NOTIMPL); } - ExitOnFailure(hr, "Failed to load shell32.dll."); + ShelExitOnFailure(hr, "Failed to load shell32.dll."); pfn = reinterpret_cast(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); - ExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); + ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); - ExitOnFailure(hr, "Failed to get known folder path."); + ShelExitOnFailure(hr, "Failed to get known folder path."); hr = StrAllocString(psczFolderPath, pwzPath, 0); - ExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); hr = PathBackslashTerminate(psczFolderPath); - ExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); LExit: if (pwzPath) @@ -255,32 +270,32 @@ static HRESULT GetDesktopShellView( // desktop web browser and then grabs its view // returns IShellView, IFolderView and related interfaces hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); - ExitOnFailure(hr, "Failed to get shell view."); + ShelExitOnFailure(hr, "Failed to get shell view."); hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); if (S_OK == hr) { hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); - ExitOnFailure(hr, "Failed to get desktop window."); + ShelExitOnFailure(hr, "Failed to get desktop window."); hr = psb->QueryActiveShellView(&psv); - ExitOnFailure(hr, "Failed to get active shell view."); + ShelExitOnFailure(hr, "Failed to get active shell view."); hr = psv->QueryInterface(riid, ppv); - ExitOnFailure(hr, "Failed to query for the desktop shell view."); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); } else if (S_FALSE == hr) { //Windows XP hr = SHGetDesktopFolder(&psf); - ExitOnFailure(hr, "Failed to get desktop folder."); + ShelExitOnFailure(hr, "Failed to get desktop folder."); hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); - ExitOnFailure(hr, "Failed to query for the desktop shell view."); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); } else { - ExitOnFailure(hr, "Failed to get desktop window."); + ShelExitOnFailure(hr, "Failed to get desktop window."); } LExit: @@ -307,16 +322,16 @@ static HRESULT GetShellDispatchFromView( // From a shell view object, gets its automation interface and from that get the shell // application object that implements IShellDispatch2 and related interfaces. hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); - ExitOnFailure(hr, "Failed to get the automation interface for shell."); + ShelExitOnFailure(hr, "Failed to get the automation interface for shell."); hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); - ExitOnFailure(hr, "Failed to get shell folder view dual."); + ShelExitOnFailure(hr, "Failed to get shell folder view dual."); hr = psfvd->get_Application(&pdisp); - ExitOnFailure(hr, "Failed to application object."); + ShelExitOnFailure(hr, "Failed to application object."); hr = pdisp->QueryInterface(riid, ppv); - ExitOnFailure(hr, "Failed to get IShellDispatch2."); + ShelExitOnFailure(hr, "Failed to get IShellDispatch2."); LExit: ReleaseObject(pdisp); diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp index 099c6ae9..63ee80ac 100644 --- a/src/dutil/sqlutil.cpp +++ b/src/dutil/sqlutil.cpp @@ -9,6 +9,21 @@ #define DBINITCONSTANTS #include "sqlutil.h" + +// Exit macros +#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) +#define SqlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) +#define SqlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) +#define SqlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) +#define SqlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SQLUTIL, e, x, s, __VA_ARGS__) +#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__) + // private prototypes static HRESULT FileSpecToString( __in const SQL_FILESPEC* psf, @@ -54,7 +69,7 @@ extern "C" HRESULT DAPI SqlConnectDatabase( //obtain access to the SQLOLEDB provider hr = ::CoCreateInstance(CLSID_SQLOLEDB, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize); - ExitOnFailure(hr, "failed to create IID_IDBInitialize object"); + SqlExitOnFailure(hr, "failed to create IID_IDBInitialize object"); // if there is an instance if (wzInstance && *wzInstance) @@ -65,7 +80,7 @@ extern "C" HRESULT DAPI SqlConnectDatabase( { hr = StrAllocString(&pwzServerInstance, wzServer, 0); } - ExitOnFailure(hr, "failed to allocate memory for the server instance"); + SqlExitOnFailure(hr, "failed to allocate memory for the server instance"); // server[\instance] rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE; @@ -124,13 +139,13 @@ extern "C" HRESULT DAPI SqlConnectDatabase( // create and set the property set hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); - ExitOnFailure(hr, "failed to get IID_IDBProperties object"); + SqlExitOnFailure(hr, "failed to get IID_IDBProperties object"); hr = pidbProperties->SetProperties(1, rgdbpsetInit); - ExitOnFailure(hr, "failed to set properties"); + SqlExitOnFailure(hr, "failed to set properties"); //initialize connection to datasource hr = pidbInitialize->Initialize(); - ExitOnFailure(hr, "failed to initialize connection to database: %ls", wzDatabase); + SqlExitOnFailure(hr, "failed to initialize connection to database: %ls", wzDatabase); hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); @@ -163,10 +178,10 @@ extern "C" HRESULT DAPI SqlStartTransaction( HRESULT hr = S_OK; hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand); - ExitOnFailure(hr, "unable to create command from session"); + SqlExitOnFailure(hr, "unable to create command from session"); hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit); - ExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal"); + SqlExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal"); hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); @@ -192,12 +207,12 @@ extern "C" HRESULT DAPI SqlEndTransaction( if (fCommit) { hr = pit->Commit(FALSE, XACTTC_SYNC, 0); - ExitOnFailure(hr, "commit of transaction failed"); + SqlExitOnFailure(hr, "commit of transaction failed"); } else { hr = pit->Abort(NULL, FALSE, FALSE); - ExitOnFailure(hr, "abort of transaction failed"); + SqlExitOnFailure(hr, "abort of transaction failed"); } LExit: @@ -231,7 +246,7 @@ extern "C" HRESULT DAPI SqlDatabaseExists( IDBCreateSession* pidbSession = NULL; hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); @@ -271,17 +286,17 @@ extern "C" HRESULT DAPI SqlSessionDatabaseExists( // query to see if the database exists // hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase); - ExitOnFailure(hr, "failed to allocate query string to ensure database exists"); + SqlExitOnFailure(hr, "failed to allocate query string to ensure database exists"); hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription); - ExitOnFailure(hr, "failed to get database list from 'master' database"); + SqlExitOnFailure(hr, "failed to get database list from 'master' database"); Assert(pirs); // // check to see if the database was returned // hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow); - ExitOnFailure(hr, "failed to get row with database name"); + SqlExitOnFailure(hr, "failed to get row with database name"); // succeeded but no database if ((DB_S_ENDOFROWSET == hr) || (0 == cRows)) @@ -324,10 +339,10 @@ extern "C" HRESULT DAPI SqlDatabaseEnsureExists( // connect to the master database to create the new database // hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - ExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); Assert(S_OK == hr); LExit: @@ -355,12 +370,12 @@ extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists( HRESULT hr = S_OK; hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); if (S_FALSE == hr) { hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - ExitOnFailure(hr, "failed to create database: %1", wzDatabase); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); } // else database already exists, return S_FALSE @@ -398,10 +413,10 @@ extern "C" HRESULT DAPI SqlCreateDatabase( // connect to the master database to create the new database // hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - ExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); Assert(S_OK == hr); LExit: @@ -433,23 +448,23 @@ extern "C" HRESULT DAPI SqlSessionCreateDatabase( if (psfDatabase) { hr = FileSpecToString(psfDatabase, &pwzDbFile); - ExitOnFailure(hr, "failed to convert db filespec to string"); + SqlExitOnFailure(hr, "failed to convert db filespec to string"); } if (psfLog) { hr = FileSpecToString(psfLog, &pwzLogFile); - ExitOnFailure(hr, "failed to convert log filespec to string"); + SqlExitOnFailure(hr, "failed to convert log filespec to string"); } hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); - ExitOnFailure(hr, "failed to escape database string"); + SqlExitOnFailure(hr, "failed to escape database string"); 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""); - ExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped); hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); - ExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery); + SqlExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery); LExit: ReleaseStr(pwzQuery); @@ -486,7 +501,7 @@ extern "C" HRESULT DAPI SqlDropDatabase( // connect to the master database to search for wzDatabase // hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - ExitOnFailure(hr, "Failed to connect to 'master' database"); + SqlExitOnFailure(hr, "Failed to connect to 'master' database"); hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription); @@ -515,18 +530,18 @@ extern "C" HRESULT DAPI SqlSessionDropDatabase( LPWSTR pwzDatabaseEscaped = NULL; hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); - ExitOnFailure(hr, "failed to escape database string"); + SqlExitOnFailure(hr, "failed to escape database string"); if (S_OK == hr) { hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped); - ExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped); hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); - ExitOnFailure(hr, "Failed to drop database"); + SqlExitOnFailure(hr, "Failed to drop database"); } LExit: @@ -567,23 +582,23 @@ extern "C" HRESULT DAPI SqlSessionExecuteQuery( // create the command // hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand); - ExitOnFailure(hr, "failed to create database session"); + SqlExitOnFailure(hr, "failed to create database session"); hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); - ExitOnFailure(hr, "failed to create command to execute session"); + SqlExitOnFailure(hr, "failed to create command to execute session"); // // set the sql text into the command // hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); - ExitOnFailure(hr, "failed to get command text object for command"); + SqlExitOnFailure(hr, "failed to get command text object for command"); hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); - ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); // // execute the command // hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); - ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); if (DB_S_ERRORSOCCURRED == hr) { @@ -642,21 +657,21 @@ extern "C" HRESULT DAPI SqlCommandExecuteQuery( // create the command // hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); - ExitOnFailure(hr, "failed to create command to execute session"); + SqlExitOnFailure(hr, "failed to create command to execute session"); // // set the sql text into the command // hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); - ExitOnFailure(hr, "failed to get command text object for command"); + SqlExitOnFailure(hr, "failed to get command text object for command"); hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); - ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); // // execute the command // hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); - ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); if (DB_S_ERRORSOCCURRED == hr) { @@ -700,14 +715,14 @@ extern "C" HRESULT DAPI SqlGetErrorInfo( // only ask for error information if the interface supports it. hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo); - ExitOnFailure(hr, "No error information was found for object."); + SqlExitOnFailure(hr, "No error information was found for object."); hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError); - ExitOnFailure(hr, "InterfaceWithError is not supported for object with error"); + SqlExitOnFailure(hr, "InterfaceWithError is not supported for object with error"); // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway hr = ::GetErrorInfo(0, &pIErrorInfoAll); - ExitOnFailure(hr, "failed to get error info"); + SqlExitOnFailure(hr, "failed to get error info"); if (S_OK == hr && pIErrorInfoAll) { @@ -787,37 +802,37 @@ static HRESULT FileSpecToString( LPWSTR pwz = NULL; hr = StrAllocString(&pwz, L"(", 1024); - ExitOnFailure(hr, "failed to allocate string for database file info"); + SqlExitOnFailure(hr, "failed to allocate string for database file info"); - ExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info"); - ExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info"); + SqlExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info"); + SqlExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info"); hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName); - ExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName); + SqlExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName); hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename); - ExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename); + SqlExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename); if (0 != psf->wzSize[0]) { hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize); - ExitOnFailure(hr, "failed to format database file info size: %s", psf->wzSize); + SqlExitOnFailure(hr, "failed to format database file info size: %ls", psf->wzSize); } if (0 != psf->wzMaxSize[0]) { hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize); - ExitOnFailure(hr, "failed to format database file info maxsize: %s", psf->wzMaxSize); + SqlExitOnFailure(hr, "failed to format database file info maxsize: %ls", psf->wzMaxSize); } if (0 != psf->wzGrow[0]) { hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow); - ExitOnFailure(hr, "failed to format database file info growth: %s", psf->wzGrow); + SqlExitOnFailure(hr, "failed to format database file info growth: %ls", psf->wzGrow); } hr = StrAllocFormatted(&pwz, L"%s)", pwz); - ExitOnFailure(hr, "failed to allocate string for file spec"); + SqlExitOnFailure(hr, "failed to allocate string for file spec"); *ppwz = pwz; pwz = NULL; // null here so it doesn't get freed below @@ -850,13 +865,13 @@ static HRESULT EscapeSqlIdentifier( if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']')) { hr = StrAllocString(&pwz, wzIdentifier, 0); - ExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier); + SqlExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier); } else { //escape it hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier); - ExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier); + SqlExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier); } *ppwz = pwz; diff --git a/src/dutil/srputil.cpp b/src/dutil/srputil.cpp index 9fc2f94a..e44536cc 100644 --- a/src/dutil/srputil.cpp +++ b/src/dutil/srputil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define SrpExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) +#define SrpExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) +#define SrpExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) +#define SrpExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) +#define SrpExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SRPUTIL, e, x, s, __VA_ARGS__) +#define SrpExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SRPUTIL, g, x, s, __VA_ARGS__) + + typedef BOOL (WINAPI *PFN_SETRESTOREPTW)( __in PRESTOREPOINTINFOW pRestorePtSpec, __out PSTATEMGRSTATUS pSMgrStatus @@ -28,7 +43,7 @@ DAPI_(HRESULT) SrpInitialize( } vpfnSRSetRestorePointW = reinterpret_cast(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW")); - ExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address."); + SrpExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address."); // If allowed, initialize COM security to enable NetworkService, // LocalService and System to make callbacks to the process @@ -37,7 +52,7 @@ DAPI_(HRESULT) SrpInitialize( if (fInitializeComSecurity) { hr = InitializeComSecurity(); - ExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore."); + SrpExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore."); } LExit: @@ -79,7 +94,7 @@ DAPI_(HRESULT) SrpCreateRestorePoint( if (!vpfnSRSetRestorePointW(&restorePoint, &status)) { - ExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point."); + SrpExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point."); } LExit: @@ -116,42 +131,42 @@ static HRESULT InitializeComSecurity() // Initialize the security descriptor. if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { - ExitWithLastError(hr, "Failed to initialize security descriptor for system restore."); + SrpExitWithLastError(hr, "Failed to initialize security descriptor for system restore."); } // Create an administrator group security identifier (SID). cbSid = sizeof(rgSidBA); if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid)) { - ExitWithLastError(hr, "Failed to create administrator SID for system restore."); + SrpExitWithLastError(hr, "Failed to create administrator SID for system restore."); } // Create a local service security identifier (SID). cbSid = sizeof(rgSidLS); if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid)) { - ExitWithLastError(hr, "Failed to create local service SID for system restore."); + SrpExitWithLastError(hr, "Failed to create local service SID for system restore."); } // Create a network service security identifier (SID). cbSid = sizeof(rgSidNS); if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid)) { - ExitWithLastError(hr, "Failed to create network service SID for system restore."); + SrpExitWithLastError(hr, "Failed to create network service SID for system restore."); } // Create a personal account security identifier (SID). cbSid = sizeof(rgSidPS); if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid)) { - ExitWithLastError(hr, "Failed to create self SID for system restore."); + SrpExitWithLastError(hr, "Failed to create self SID for system restore."); } // Create a local service security identifier (SID). cbSid = sizeof(rgSidSY); if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid)) { - ExitWithLastError(hr, "Failed to create local system SID for system restore."); + SrpExitWithLastError(hr, "Failed to create local system SID for system restore."); } // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and @@ -203,29 +218,29 @@ static HRESULT InitializeComSecurity() // Create an access control list (ACL) using this ACE list. er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl); - ExitOnWin32Error(er, hr, "Failed to create ACL for system restore."); + SrpExitOnWin32Error(er, hr, "Failed to create ACL for system restore."); // Set the security descriptor owner to Administrators. if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE)) { - ExitWithLastError(hr, "Failed to set administrators owner for system restore."); + SrpExitWithLastError(hr, "Failed to set administrators owner for system restore."); } // Set the security descriptor group to Administrators. if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE)) { - ExitWithLastError(hr, "Failed to set administrators group access for system restore."); + SrpExitWithLastError(hr, "Failed to set administrators group access for system restore."); } // Set the discretionary access control list (DACL) to the ACL. if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) { - ExitWithLastError(hr, "Failed to set DACL for system restore."); + SrpExitWithLastError(hr, "Failed to set DACL for system restore."); } // Note that an explicit security descriptor is being passed in. 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); - ExitOnFailure(hr, "Failed to initialize COM security for system restore."); + SrpExitOnFailure(hr, "Failed to initialize COM security for system restore."); LExit: if (pAcl) diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp index c1d701d3..daa090c9 100644 --- a/src/dutil/strutil.cpp +++ b/src/dutil/strutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define StrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) +#define StrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) +#define StrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) +#define StrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) +#define StrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_STRUTIL, e, x, s, __VA_ARGS__) +#define StrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_STRUTIL, g, x, s, __VA_ARGS__) + #define ARRAY_GROWTH_SIZE 5 // Forward declarations. @@ -84,7 +99,7 @@ static HRESULT AllocHelper( if (cch >= MAXDWORD / sizeof(WCHAR)) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); } if (*ppwz) @@ -93,7 +108,7 @@ static HRESULT AllocHelper( { LPVOID pvNew = NULL; hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew); - ExitOnFailure(hr, "Failed to reallocate string"); + StrExitOnFailure(hr, "Failed to reallocate string"); pwz = static_cast(pvNew); } else @@ -106,7 +121,7 @@ static HRESULT AllocHelper( pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); } - ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); *ppwz = pwz; LExit: @@ -131,12 +146,12 @@ HRESULT DAPI StrTrimCapacity( SIZE_T cchLen = 0; hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); ++cchLen; // Add 1 for null-terminator hr = StrAlloc(ppwz, cchLen); - ExitOnFailure(hr, "Failed to reallocate string"); + StrExitOnFailure(hr, "Failed to reallocate string"); LExit: return hr; @@ -181,7 +196,7 @@ HRESULT DAPI StrTrimWhitespace( } hr = StrAllocString(&sczResult, wzSource, i); - ExitOnFailure(hr, "Failed to copy result string"); + StrExitOnFailure(hr, "Failed to copy result string"); // Output result *ppwz = sczResult; @@ -212,7 +227,7 @@ extern "C" HRESULT DAPI StrAnsiAlloc( if (cch >= MAXDWORD / sizeof(WCHAR)) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); } if (*ppsz) @@ -224,7 +239,7 @@ extern "C" HRESULT DAPI StrAnsiAlloc( psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); } - ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); *ppsz = psz; LExit: @@ -252,12 +267,12 @@ HRESULT DAPI StrAnsiTrimCapacity( #pragma prefast(disable:25068) hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); #pragma prefast(pop) - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); ++cchLen; // Add 1 for null-terminator hr = StrAnsiAlloc(ppz, cchLen); - ExitOnFailure(hr, "Failed to reallocate string"); + StrExitOnFailure(hr, "Failed to reallocate string"); LExit: return hr; @@ -302,7 +317,7 @@ HRESULT DAPI StrAnsiTrimWhitespace( } hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i); - ExitOnFailure(hr, "Failed to copy result string"); + StrExitOnFailure(hr, "Failed to copy result string"); // Output result *ppz = sczResult; @@ -375,7 +390,7 @@ static HRESULT AllocStringHelper( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(WCHAR); //convert the count in bytes to count in characters } @@ -387,13 +402,13 @@ static HRESULT AllocStringHelper( SIZE_T cchNeeded; hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - ExitOnFailure(hr, "source string is too long"); + StrExitOnFailure(hr, "source string is too long"); if (cch < cchNeeded) { cch = cchNeeded; hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - ExitOnFailure(hr, "failed to allocate string from string."); + StrExitOnFailure(hr, "failed to allocate string from string."); } // copy everything (the NULL terminator will be included) @@ -431,7 +446,7 @@ extern "C" HRESULT DAPI StrAnsiAllocString( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(CHAR); //convert the count in bytes to count in characters } @@ -441,7 +456,7 @@ extern "C" HRESULT DAPI StrAnsiAllocString( cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL); if (0 == cchDest) { - ExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource); + StrExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource); } --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below @@ -457,7 +472,7 @@ extern "C" HRESULT DAPI StrAnsiAllocString( if (cch >= MAXDWORD / sizeof(WCHAR)) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); } if (*ppsz) @@ -468,14 +483,14 @@ extern "C" HRESULT DAPI StrAnsiAllocString( { psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); } - ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); *ppsz = psz; } if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL)) { - ExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource); + StrExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource); } (*ppsz)[cchDest] = L'\0'; @@ -511,7 +526,7 @@ extern "C" HRESULT DAPI StrAllocStringAnsi( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(WCHAR); //convert the count in bytes to count in characters } @@ -521,7 +536,7 @@ extern "C" HRESULT DAPI StrAllocStringAnsi( cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0); if (0 == cchDest) { - ExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource); + StrExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource); } --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below @@ -537,7 +552,7 @@ extern "C" HRESULT DAPI StrAllocStringAnsi( if (cch >= MAXDWORD / sizeof(WCHAR)) { hr = E_OUTOFMEMORY; - ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); } if (*ppwz) @@ -549,14 +564,14 @@ extern "C" HRESULT DAPI StrAllocStringAnsi( pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); } - ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); *ppwz = pwz; } if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch)) { - ExitWithLastError(hr, "failed to convert to unicode: %s", szSource); + StrExitWithLastError(hr, "failed to convert to unicode: %s", szSource); } (*ppwz)[cchDest] = L'\0'; @@ -589,7 +604,7 @@ HRESULT DAPI StrAnsiAllocStringAnsi( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(CHAR); //convert the count in bytes to count in characters } @@ -601,13 +616,13 @@ HRESULT DAPI StrAnsiAllocStringAnsi( SIZE_T cchNeeded; hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - ExitOnFailure(hr, "source string is too long"); + StrExitOnFailure(hr, "source string is too long"); if (cch < cchNeeded) { cch = cchNeeded; hr = StrAnsiAlloc(ppsz, cch); - ExitOnFailure(hr, "failed to allocate string from string."); + StrExitOnFailure(hr, "failed to allocate string from string."); } // copy everything (the NULL terminator will be included) @@ -647,12 +662,12 @@ extern "C" HRESULT DAPI StrAllocPrefix( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(WCHAR); //convert the count in bytes to count in characters hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); } Assert(cchLen <= cch); @@ -660,14 +675,14 @@ extern "C" HRESULT DAPI StrAllocPrefix( if (0 == cchPrefix) { hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast(&cchPrefix)); - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); } if (cch - cchLen < cchPrefix + 1) { cch = cchPrefix + cchLen + 1; hr = StrAlloc(ppwz, cch); - ExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix); + StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix); } if (*ppwz) @@ -681,7 +696,7 @@ extern "C" HRESULT DAPI StrAllocPrefix( else { hr = E_UNEXPECTED; - ExitOnFailure(hr, "for some reason our buffer is still null"); + StrExitOnFailure(hr, "for some reason our buffer is still null"); } LExit: @@ -753,12 +768,12 @@ static HRESULT AllocConcatHelper( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(WCHAR); //convert the count in bytes to count in characters hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); } Assert(cchLen <= cch); @@ -766,14 +781,14 @@ static HRESULT AllocConcatHelper( if (0 == cchSource) { hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); } if (cch - cchLen < cchSource + 1) { cch = (cchSource + cchLen + 1) * 2; hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - ExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource); + StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource); } if (*ppwz) @@ -783,7 +798,7 @@ static HRESULT AllocConcatHelper( else { hr = E_UNEXPECTED; - ExitOnFailure(hr, "for some reason our buffer is still null"); + StrExitOnFailure(hr, "for some reason our buffer is still null"); } LExit: @@ -816,7 +831,7 @@ extern "C" HRESULT DAPI StrAnsiAllocConcat( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(CHAR); // convert the count in bytes to count in characters @@ -824,7 +839,7 @@ extern "C" HRESULT DAPI StrAnsiAllocConcat( #pragma prefast(disable:25068) hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); #pragma prefast(pop) - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); } Assert(cchLen <= cch); @@ -835,14 +850,14 @@ extern "C" HRESULT DAPI StrAnsiAllocConcat( #pragma prefast(disable:25068) hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); #pragma prefast(pop) - ExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnFailure(hr, "Failed to calculate length of string"); } if (cch - cchLen < cchSource + 1) { cch = (cchSource + cchLen + 1) * 2; hr = StrAnsiAlloc(ppz, cch); - ExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource); + StrExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource); } if (*ppz) @@ -855,7 +870,7 @@ extern "C" HRESULT DAPI StrAnsiAllocConcat( else { hr = E_UNEXPECTED; - ExitOnFailure(hr, "for some reason our buffer is still null"); + StrExitOnFailure(hr, "for some reason our buffer is still null"); } LExit: @@ -908,7 +923,7 @@ extern "C" HRESULT __cdecl StrAllocConcatFormatted( va_start(args, wzFormat); hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args); va_end(args); - ExitOnFailure(hr, "Failed to allocate formatted string"); + StrExitOnFailure(hr, "Failed to allocate formatted string"); hr = StrAllocConcat(ppwz, sczFormatted, 0); @@ -942,7 +957,7 @@ extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure( va_start(args, wzFormat); hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args); va_end(args); - ExitOnFailure(hr, "Failed to allocate formatted string"); + StrExitOnFailure(hr, "Failed to allocate formatted string"); hr = StrAllocConcatSecure(ppwz, sczFormatted, 0); @@ -1068,7 +1083,7 @@ static HRESULT AllocFormattedArgsHelper( if (-1 == cbOriginal) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters @@ -1080,7 +1095,7 @@ static HRESULT AllocFormattedArgsHelper( cch = 256; hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); } // format the message (grow until it fits or there is a failure) @@ -1104,12 +1119,12 @@ static HRESULT AllocFormattedArgsHelper( cch *= 2; hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); hr = S_FALSE; } } while (S_FALSE == hr); - ExitOnFailure(hr, "failed to format string"); + StrExitOnFailure(hr, "failed to format string"); LExit: if (pwzOriginal && fZeroOnRealloc) @@ -1148,7 +1163,7 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnFailure(hr, "failed to get size of destination string"); } cch /= sizeof(CHAR); //convert the count in bytes to count in characters @@ -1159,7 +1174,7 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( { cch = 256; hr = StrAnsiAlloc(ppsz, cch); - ExitOnFailure(hr, "failed to allocate string to format: %s", szFormat); + StrExitOnFailure(hr, "failed to allocate string to format: %s", szFormat); } // format the message (grow until it fits or there is a failure) @@ -1183,11 +1198,11 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( } cch *= 2; hr = StrAnsiAlloc(ppsz, cch); - ExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat); + StrExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat); hr = S_FALSE; } } while (S_FALSE == hr); - ExitOnFailure(hr, "failed to format string"); + StrExitOnFailure(hr, "failed to format string"); LExit: ReleaseStr(pszOriginal); @@ -1224,11 +1239,11 @@ extern "C" HRESULT DAPI StrAllocFromError( if (0 == cchMessage) { - ExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError); + StrExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError); } hr = StrAllocString(ppwzMessage, reinterpret_cast(pvMessage), cchMessage); - ExitOnFailure(hr, "Failed to allocate string for message."); + StrExitOnFailure(hr, "Failed to allocate string for message."); LExit: if (pvMessage) @@ -1308,7 +1323,7 @@ extern "C" HRESULT DAPI StrFree( Assert(p); HRESULT hr = MemFree(p); - ExitOnFailure(hr, "failed to free string"); + StrExitOnFailure(hr, "failed to free string"); LExit: return hr; @@ -1332,7 +1347,7 @@ extern "C" HRESULT DAPI StrReplaceStringAll( do { hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString); - ExitOnFailure(hr, "Failed to replace substring"); + StrExitOnFailure(hr, "Failed to replace substring"); } while (S_OK == hr); @@ -1373,21 +1388,21 @@ extern "C" HRESULT DAPI StrReplaceString( } hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex); - ExitOnFailure(hr, "Failed to diff pointers."); + StrExitOnFailure(hr, "Failed to diff pointers."); hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal); - ExitOnFailure(hr, "Failed to duplicate string."); + StrExitOnFailure(hr, "Failed to duplicate string."); pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0'; hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0); - ExitOnFailure(hr, "Failed to append new string."); + StrExitOnFailure(hr, "Failed to append new string."); hr = StrAllocConcat(&pwzBuffer, wzSubLocation + wcslen(wzOldSubString), 0); - ExitOnFailure(hr, "Failed to append post string."); + StrExitOnFailure(hr, "Failed to append post string."); hr = StrFree(*ppwzOriginal); - ExitOnFailure(hr, "Failed to free original string."); + StrExitOnFailure(hr, "Failed to free original string."); *ppwzOriginal = pwzBuffer; *pdwStartIndex = *pdwStartIndex + static_cast(wcslen(wzNewSubString)); @@ -1477,10 +1492,10 @@ HRESULT DAPI StrAllocHexEncode( SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1); hr = StrAlloc(ppwzDest, cchSource); - ExitOnFailure(hr, "Failed to allocate hex string."); + StrExitOnFailure(hr, "Failed to allocate hex string."); hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource); - ExitOnFailure(hr, "Failed to encode hex string."); + StrExitOnFailure(hr, "Failed to encode hex string."); LExit: return hr; @@ -1509,7 +1524,7 @@ extern "C" HRESULT DAPI StrHexDecode( if (cbDest < cchSource / 2) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %u into %u bytes.", wzSource, cchSource, cbDest); + StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %u into %u bytes.", wzSource, cchSource, cbDest); } for (i = 0; i < cchSource / 2; ++i) @@ -1547,20 +1562,20 @@ extern "C" HRESULT DAPI StrAllocHexDecode( DWORD cb = 0; hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch); - ExitOnFailure(hr, "Failed to calculate length of source string."); + StrExitOnFailure(hr, "Failed to calculate length of source string."); if (cch % 2) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded."); + StrExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded."); } cb = static_cast(cch / 2); pb = static_cast(MemAlloc(cb, TRUE)); - ExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode."); + StrExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode."); hr = StrHexDecode(wzSource, pb, cb); - ExitOnFailure(hr, "Failed to decode source string."); + StrExitOnFailure(hr, "Failed to decode source string."); *ppbDest = pb; pb = NULL; @@ -1637,7 +1652,7 @@ extern "C" HRESULT DAPI StrAllocBase85Encode( ++cchDest; // add room for null terminator hr = StrAlloc(pwzDest, cchDest); - ExitOnFailure(hr, "failed to allocate destination string"); + StrExitOnFailure(hr, "failed to allocate destination string"); wzDest = *pwzDest; @@ -1740,7 +1755,7 @@ extern "C" HRESULT DAPI StrAllocBase85Decode( } *ppbDest = static_cast(MemAlloc(cbDest, FALSE)); - ExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string"); + StrExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string"); pbDest = *ppbDest; *pcbDest = cbDest; @@ -1860,7 +1875,7 @@ extern "C" HRESULT DAPI MultiSzLen( DWORD_PTR dwMaxSize = 0; hr = StrMaxLength(pwzMultiSz, &dwMaxSize); - ExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length"); + StrExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length"); *pcch = 0; while (*pcch < dwMaxSize) @@ -1914,7 +1929,7 @@ extern "C" HRESULT DAPI MultiSzPrepend( else { hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - ExitOnFailure(hr, "failed to get length of multisz"); + StrExitOnFailure(hr, "failed to get length of multisz"); } cchInsert = lstrlenW(pwzInsert); @@ -1923,11 +1938,11 @@ extern "C" HRESULT DAPI MultiSzPrepend( // Allocate the result buffer hr = StrAlloc(&pwzResult, cchResult + 1); - ExitOnFailure(hr, "failed to allocate result string"); + StrExitOnFailure(hr, "failed to allocate result string"); // Prepend hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert); - ExitOnFailure(hr, "failed to copy prepend string: %ls", pwzInsert); + StrExitOnFailure(hr, "failed to copy prepend string: %ls", pwzInsert); // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in if (0 == cchMultiSz) @@ -1983,7 +1998,7 @@ extern "C" HRESULT DAPI MultiSzFindSubstring( SIZE_T cchProgress = 0; hr = MultiSzLen(pwzMultiSz, &cchMultiSz); - ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); // Find the string containing the sub string hr = S_OK; @@ -2049,7 +2064,7 @@ extern "C" HRESULT DAPI MultiSzFindString( SIZE_T cchProgress = 0; hr = MultiSzLen(pwzMultiSz, &cchMutliSz); - ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); // Find the string hr = S_OK; @@ -2116,7 +2131,7 @@ extern "C" HRESULT DAPI MultiSzRemoveString( SIZE_T cchProgress = 0; hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); // Find the index we want to remove hr = S_OK; @@ -2159,7 +2174,7 @@ extern "C" HRESULT DAPI MultiSzRemoveString( if (cchProgress > cchMultiSz) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ"); + StrExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ"); } // Move on to the next character @@ -2181,7 +2196,7 @@ extern "C" HRESULT DAPI MultiSzInsertString( __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, __inout_opt SIZE_T* pcchMultiSz, __in DWORD_PTR dwIndex, - __in __nullnullterminated LPCWSTR pwzInsert + __in_z LPCWSTR pwzInsert ) { Assert(ppwzMultiSz && pwzInsert && *pwzInsert); @@ -2202,7 +2217,7 @@ extern "C" HRESULT DAPI MultiSzInsertString( else { hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - ExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); } // Find the index we want to insert at @@ -2220,7 +2235,7 @@ extern "C" HRESULT DAPI MultiSzInsertString( if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz) { hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND); - ExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex); + StrExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex); } // Move on to the next string @@ -2235,7 +2250,7 @@ extern "C" HRESULT DAPI MultiSzInsertString( cchResult = cchMultiSz + cchString + 1; hr = StrAlloc(&pwzResult, cchResult); - ExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); + StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); // Copy the part before the insert ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR)); @@ -2273,7 +2288,7 @@ MultiSzReplaceString - replaces string at the specified index with a new one extern "C" HRESULT DAPI MultiSzReplaceString( __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, __in DWORD_PTR dwIndex, - __in __nullnullterminated LPCWSTR pwzString + __in_z LPCWSTR pwzString ) { Assert(ppwzMultiSz && pwzString && *pwzString); @@ -2281,10 +2296,10 @@ extern "C" HRESULT DAPI MultiSzReplaceString( HRESULT hr = S_OK; hr = MultiSzRemoveString(ppwzMultiSz, dwIndex); - ExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex); + StrExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex); hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString); - ExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex); + StrExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex); LExit: return hr; @@ -2344,7 +2359,7 @@ extern "C" HRESULT DAPI StrStringToInt16( LONGLONG ll = 0; hr = StrStringToInt64(wzIn, cchIn, &ll); - ExitOnFailure(hr, "Failed to parse int64."); + StrExitOnFailure(hr, "Failed to parse int64."); if (SHORT_MAX < ll || SHORT_MIN > ll) { @@ -2370,7 +2385,7 @@ extern "C" HRESULT DAPI StrStringToUInt16( ULONGLONG ull = 0; hr = StrStringToUInt64(wzIn, cchIn, &ull); - ExitOnFailure(hr, "Failed to parse uint64."); + StrExitOnFailure(hr, "Failed to parse uint64."); if (USHORT_MAX < ull) { @@ -2396,7 +2411,7 @@ extern "C" HRESULT DAPI StrStringToInt32( LONGLONG ll = 0; hr = StrStringToInt64(wzIn, cchIn, &ll); - ExitOnFailure(hr, "Failed to parse int64."); + StrExitOnFailure(hr, "Failed to parse int64."); if (INT_MAX < ll || INT_MIN > ll) { @@ -2422,7 +2437,7 @@ extern "C" HRESULT DAPI StrStringToUInt32( ULONGLONG ull = 0; hr = StrStringToUInt64(wzIn, cchIn, &ull); - ExitOnFailure(hr, "Failed to parse uint64."); + StrExitOnFailure(hr, "Failed to parse uint64."); if (UINT_MAX < ull) { @@ -2607,13 +2622,13 @@ extern "C" HRESULT DAPI StrArrayAllocString( UINT cNewStrArray; hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray); - ExitOnFailure(hr, "Failed to increment the string array element count."); + StrExitOnFailure(hr, "Failed to increment the string array element count."); hr = MemEnsureArraySize(reinterpret_cast(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE); - ExitOnFailure(hr, "Failed to allocate memory for the string array."); + StrExitOnFailure(hr, "Failed to allocate memory for the string array."); hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource); - ExitOnFailure(hr, "Failed to allocate and assign the string."); + StrExitOnFailure(hr, "Failed to allocate and assign the string."); *pcStrArray = cNewStrArray; @@ -2639,12 +2654,12 @@ extern "C" HRESULT DAPI StrArrayFree( if (NULL != rgsczStrArray[i]) { hr = StrFree(rgsczStrArray[i]); - ExitOnFailure(hr, "Failed to free the string at index %u.", i); + StrExitOnFailure(hr, "Failed to free the string at index %u.", i); } } hr = MemFree(rgsczStrArray); - ExitOnFailure(hr, "Failed to free memory for the string array."); + StrExitOnFailure(hr, "Failed to free memory for the string array."); LExit: return hr; @@ -2667,12 +2682,12 @@ extern "C" HRESULT DAPI StrSplitAllocArray( // Copy wzSource so it is not modified. hr = StrAllocString(&sczCopy, wzSource, 0); - ExitOnFailure(hr, "Failed to copy the source string."); + StrExitOnFailure(hr, "Failed to copy the source string."); for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) { hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0); - ExitOnFailure(hr, "Failed to add the string to the string array."); + StrExitOnFailure(hr, "Failed to add the string to the string array."); } LExit: @@ -2696,20 +2711,20 @@ static HRESULT StrAllocStringMapInvariant( HRESULT hr = S_OK; hr = StrAllocString(pscz, wzSource, cchSource); - ExitOnFailure(hr, "Failed to allocate a copy of the source string."); + StrExitOnFailure(hr, "Failed to allocate a copy of the source string."); if (0 == cchSource) { // Need the actual string size for LCMapString. This includes the null-terminator // but LCMapString doesn't care either way. hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast(&cchSource)); - ExitOnFailure(hr, "Failed to get the length of the string."); + StrExitOnFailure(hr, "Failed to get the length of the string."); } // Convert the copy of the string to upper or lower case in-place. if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, cchSource, *pscz, cchSource)) { - ExitWithLastError(hr, "Failed to convert the string case."); + StrExitWithLastError(hr, "Failed to convert the string case."); } LExit: @@ -2734,7 +2749,7 @@ extern "C" DAPI_(HRESULT) StrSecureZeroString( if (-1 == cch) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Failed to get size of string"); + StrExitOnFailure(hr, "Failed to get size of string"); } else { diff --git a/src/dutil/svcutil.cpp b/src/dutil/svcutil.cpp index 9da2b5b3..1a39bfee 100644 --- a/src/dutil/svcutil.cpp +++ b/src/dutil/svcutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define SvcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) +#define SvcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) +#define SvcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) +#define SvcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) +#define SvcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SVCUTIL, e, x, s, __VA_ARGS__) +#define SvcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SVCUTIL, g, x, s, __VA_ARGS__) + /******************************************************************** SvcQueryConfig - queries the configuration of a service @@ -21,16 +36,16 @@ extern "C" HRESULT DAPI SvcQueryConfig( if (ERROR_INSUFFICIENT_BUFFER == er) { pConfig = static_cast(MemAlloc(cbConfig, TRUE)); - ExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration."); + SvcExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration."); if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig)) { - ExitWithLastError(hr, "Failed to read service configuration."); + SvcExitWithLastError(hr, "Failed to read service configuration."); } } else { - ExitOnWin32Error(er, hr, "Failed to query service configuration."); + SvcExitOnWin32Error(er, hr, "Failed to query service configuration."); } } diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 9b9bf15e..d87e997d 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -693,7 +693,7 @@ DAPI_(HRESULT) ThemeCreateParentWindow( } LExit: - MemFree(pMonitorContext); + ReleaseMem(pMonitorContext); return hr; } @@ -1514,7 +1514,7 @@ LExit: DAPI_(HRESULT) ThemeGetTextControl( __in const THEME* pTheme, __in DWORD dwControl, - __out_z LPWSTR* psczText + __inout_z LPWSTR* psczText ) { HRESULT hr = S_OK; @@ -1729,6 +1729,7 @@ static HRESULT ParseImage( LPWSTR sczImageFile = NULL; int iResourceId = 0; Gdiplus::Bitmap* pBitmap = NULL; + *phImage = NULL; hr = XmlGetAttribute(pElement, L"ImageResource", &bstr); ThmExitOnFailure(hr, "Failed to get image resource attribute."); @@ -1801,6 +1802,7 @@ static HRESULT ParseIcon( BSTR bstr = NULL; LPWSTR sczImageFile = NULL; int iResourceId = 0; + *phIcon = NULL; hr = XmlGetAttribute(pElement, L"IconResource", &bstr); ThmExitOnFailure(hr, "Failed to get icon resource attribute."); @@ -4720,7 +4722,7 @@ static HRESULT ShowControl( hr = S_OK; - Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1)); + Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || (sczText && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1))); } } diff --git a/src/dutil/timeutil.cpp b/src/dutil/timeutil.cpp index dacb2660..b7953c94 100644 --- a/src/dutil/timeutil.cpp +++ b/src/dutil/timeutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define TimeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) +#define TimeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) +#define TimeExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) +#define TimeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) +#define TimeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_TIMEUTIL, e, x, s, __VA_ARGS__) +#define TimeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_TIMEUTIL, g, x, s, __VA_ARGS__) + const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; const 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" }; enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone }; @@ -39,7 +54,7 @@ extern "C" HRESULT DAPI TimeFromString( LPWSTR pwzEnd = NULL; hr = StrAllocString(&pwzTime, wzTime, 0); - ExitOnFailure(hr, "Failed to copy time."); + TimeExitOnFailure(hr, "Failed to copy time."); pwzStart = pwzEnd = pwzTime; while (pwzEnd && *pwzEnd) @@ -58,7 +73,7 @@ extern "C" HRESULT DAPI TimeFromString( { case DayOfWeek: hr = DayFromString(pwzStart, &sysTime.wDayOfWeek); - ExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart); + TimeExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart); break; case DayOfMonth: @@ -67,7 +82,7 @@ extern "C" HRESULT DAPI TimeFromString( case MonthOfYear: hr = MonthFromString(pwzStart, &sysTime.wMonth); - ExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart); + TimeExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart); break; case Year: @@ -104,7 +119,7 @@ extern "C" HRESULT DAPI TimeFromString( if (!::SystemTimeToFileTime(&sysTime, pFileTime)) { - ExitWithLastError(hr, "Failed to convert system time to file time."); + TimeExitWithLastError(hr, "Failed to convert system time to file time."); } LExit: @@ -134,7 +149,7 @@ extern "C" HRESULT DAPI TimeFromString3339( LPWSTR pwzEnd = NULL; hr = StrAllocString(&pwzTime, wzTime, 0); - ExitOnFailure(hr, "Failed to copy time."); + TimeExitOnFailure(hr, "Failed to copy time."); pwzStart = pwzEnd = pwzTime; while (pwzEnd && *pwzEnd) @@ -188,7 +203,7 @@ extern "C" HRESULT DAPI TimeFromString3339( if (!::SystemTimeToFileTime(&sysTime, pFileTime)) { - ExitWithLastError(hr, "Failed to convert system time to file time."); + TimeExitWithLastError(hr, "Failed to convert system time to file time."); } LExit: @@ -291,29 +306,29 @@ HRESULT DAPI TimeSystemToDateTimeString( iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0); if (0 >= iLenDate) { - ExitWithLastError(hr, "Failed to get date format with NULL"); + TimeExitWithLastError(hr, "Failed to get date format with NULL"); } iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0); if (0 >= iLenTime) { - ExitWithLastError(hr, "Failed to get time format with NULL"); + TimeExitWithLastError(hr, "Failed to get time format with NULL"); } // Between both lengths we account for 2 null terminators, and only need one, so we subtract one hr = StrAlloc(ppwz, iLenDate + iLenTime - 1); - ExitOnFailure(hr, "Failed to allocate string"); + TimeExitOnFailure(hr, "Failed to allocate string"); if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate)) { - ExitWithLastError(hr, "Failed to get date format with buffer"); + TimeExitWithLastError(hr, "Failed to get date format with buffer"); } // Space to separate them (*ppwz)[iLenDate - 1] = ' '; if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime)) { - ExitWithLastError(hr, "Failed to get time format with buffer"); + TimeExitWithLastError(hr, "Failed to get time format with buffer"); } LExit: diff --git a/src/dutil/uncutil.cpp b/src/dutil/uncutil.cpp index 6deb43bd..415ea198 100644 --- a/src/dutil/uncutil.cpp +++ b/src/dutil/uncutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define UncExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) +#define UncExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) +#define UncExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) +#define UncExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) +#define UncExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_UNCUTIL, e, x, s, __VA_ARGS__) +#define UncExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_UNCUTIL, g, x, s, __VA_ARGS__) + DAPI_(HRESULT) UncConvertFromMountedDrive( __inout LPWSTR *psczUNCPath, __in LPCWSTR sczMountedDrivePath @@ -14,7 +29,7 @@ DAPI_(HRESULT) UncConvertFromMountedDrive( // Only copy drive letter and colon hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2); - ExitOnFailure(hr, "Failed to copy drive"); + UncExitOnFailure(hr, "Failed to copy drive"); // ERROR_NOT_CONNECTED means it's not a mapped drive er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength); @@ -23,7 +38,7 @@ DAPI_(HRESULT) UncConvertFromMountedDrive( er = ERROR_SUCCESS; hr = StrAlloc(psczUNCPath, dwLength); - ExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength); + UncExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength); er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength); if (ERROR_CONNECTION_UNAVAIL == er) @@ -31,11 +46,11 @@ DAPI_(HRESULT) UncConvertFromMountedDrive( // 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 er = ERROR_SUCCESS; } - ExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive); + UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive); // Skip drive letter and colon hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0); - ExitOnFailure(hr, "Failed to copy rest of database path"); + UncExitOnFailure(hr, "Failed to copy rest of database path"); } else { @@ -44,7 +59,7 @@ DAPI_(HRESULT) UncConvertFromMountedDrive( er = ERROR_NO_DATA; } - ExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive); + UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive); } LExit: diff --git a/src/dutil/uriutil.cpp b/src/dutil/uriutil.cpp index fc192b3f..7913c22e 100644 --- a/src/dutil/uriutil.cpp +++ b/src/dutil/uriutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define UriExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) +#define UriExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) +#define UriExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) +#define UriExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) +#define UriExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_URIUTIL, e, x, s, __VA_ARGS__) +#define UriExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_URIUTIL, g, x, s, __VA_ARGS__) + + // // UriCanonicalize - canonicalizes a URI. // @@ -16,11 +31,11 @@ extern "C" HRESULT DAPI UriCanonicalize( if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE)) { - ExitWithLastError(hr, "Failed to canonicalize URI."); + UriExitWithLastError(hr, "Failed to canonicalize URI."); } hr = StrAllocString(psczUri, wz, cch); - ExitOnFailure(hr, "Failed copy canonicalized URI."); + UriExitOnFailure(hr, "Failed copy canonicalized URI."); LExit: return hr; @@ -83,7 +98,7 @@ extern "C" HRESULT DAPI UriCrack( if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components)) { - ExitWithLastError(hr, "Failed to crack URI."); + UriExitWithLastError(hr, "Failed to crack URI."); } if (pScheme) @@ -94,7 +109,7 @@ extern "C" HRESULT DAPI UriCrack( if (psczHostName) { hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength); - ExitOnFailure(hr, "Failed to copy host name."); + UriExitOnFailure(hr, "Failed to copy host name."); } if (pPort) @@ -105,25 +120,25 @@ extern "C" HRESULT DAPI UriCrack( if (psczUser) { hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength); - ExitOnFailure(hr, "Failed to copy user name."); + UriExitOnFailure(hr, "Failed to copy user name."); } if (psczPassword) { hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength); - ExitOnFailure(hr, "Failed to copy password."); + UriExitOnFailure(hr, "Failed to copy password."); } if (psczPath) { hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength); - ExitOnFailure(hr, "Failed to copy path."); + UriExitOnFailure(hr, "Failed to copy path."); } if (psczQueryString) { hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength); - ExitOnFailure(hr, "Failed to copy query string."); + UriExitOnFailure(hr, "Failed to copy query string."); } LExit: @@ -142,7 +157,7 @@ extern "C" HRESULT DAPI UriCrackEx( HRESULT hr = S_OK; hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString); - ExitOnFailure(hr, "Failed to crack URI."); + UriExitOnFailure(hr, "Failed to crack URI."); LExit: return hr; @@ -195,11 +210,11 @@ extern "C" HRESULT DAPI UriCreate( if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch)) { - ExitWithLastError(hr, "Failed to create URI."); + UriExitWithLastError(hr, "Failed to create URI."); } hr = StrAllocString(psczUri, wz, cch); - ExitOnFailure(hr, "Failed copy created URI."); + UriExitOnFailure(hr, "Failed copy created URI."); LExit: return hr; @@ -227,13 +242,13 @@ extern "C" HRESULT DAPI UriGetServerAndResource( LPWSTR sczQueryString = NULL; hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString); - ExitOnFailure(hr, "Failed to crack URI."); + UriExitOnFailure(hr, "Failed to crack URI."); hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL); - ExitOnFailure(hr, "Failed to allocate server URI."); + UriExitOnFailure(hr, "Failed to allocate server URI."); hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString); - ExitOnFailure(hr, "Failed to allocate resource URI."); + UriExitOnFailure(hr, "Failed to allocate resource URI."); LExit: ReleaseStr(sczQueryString); @@ -265,13 +280,13 @@ extern "C" HRESULT DAPI UriFile( if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc)) { - ExitWithLastError(hr, "Failed to crack URI."); + UriExitWithLastError(hr, "Failed to crack URI."); } // Copy only the file name. Fortunately, PathFile() understands that // forward slashes can be directory separators like backslashes. hr = StrAllocString(psczFile, PathFile(wz), 0); - ExitOnFailure(hr, "Failed to copy file name"); + UriExitOnFailure(hr, "Failed to copy file name"); LExit: return hr; @@ -367,7 +382,7 @@ extern "C" HRESULT DAPI UriRoot( LPCWSTR pwcSlash = NULL; hr = UriProtocol(wzUri, &protocol); - ExitOnFailure(hr, "Invalid URI."); + UriExitOnFailure(hr, "Invalid URI."); switch (protocol) { @@ -377,7 +392,7 @@ extern "C" HRESULT DAPI UriRoot( if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9]) { hr = StrAlloc(ppwzRoot, 4); - ExitOnFailure(hr, "Failed to allocate string for root of URI."); + UriExitOnFailure(hr, "Failed to allocate string for root of URI."); *ppwzRoot[0] = wzUri[8]; *ppwzRoot[1] = L':'; *ppwzRoot[2] = L'\\'; @@ -386,7 +401,7 @@ extern "C" HRESULT DAPI UriRoot( else { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid file path in URI."); + UriExitOnFailure(hr, "Invalid file path in URI."); } } else // UNC share @@ -395,23 +410,23 @@ extern "C" HRESULT DAPI UriRoot( if (!pwcSlash) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid server name in URI."); + UriExitOnFailure(hr, "Invalid server name in URI."); } else { hr = StrAllocString(ppwzRoot, L"\\\\", 64); - ExitOnFailure(hr, "Failed to allocate string for root of URI."); + UriExitOnFailure(hr, "Failed to allocate string for root of URI."); pwcSlash = wcschr(pwcSlash + 1, L'/'); if (pwcSlash) { hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8); - ExitOnFailure(hr, "Failed to add server/share to root of URI."); + UriExitOnFailure(hr, "Failed to add server/share to root of URI."); } else { hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0); - ExitOnFailure(hr, "Failed to add server/share to root of URI."); + UriExitOnFailure(hr, "Failed to add server/share to root of URI."); } // replace all slashes with backslashes to be truly UNC. @@ -431,12 +446,12 @@ extern "C" HRESULT DAPI UriRoot( if (pwcSlash) { hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); - ExitOnFailure(hr, "Failed allocate root from URI."); + UriExitOnFailure(hr, "Failed allocate root from URI."); } else { hr = StrAllocString(ppwzRoot, wzUri, 0); - ExitOnFailure(hr, "Failed allocate root from URI."); + UriExitOnFailure(hr, "Failed allocate root from URI."); } break; @@ -445,18 +460,18 @@ extern "C" HRESULT DAPI UriRoot( if (pwcSlash) { hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); - ExitOnFailure(hr, "Failed allocate root from URI."); + UriExitOnFailure(hr, "Failed allocate root from URI."); } else { hr = StrAllocString(ppwzRoot, wzUri, 0); - ExitOnFailure(hr, "Failed allocate root from URI."); + UriExitOnFailure(hr, "Failed allocate root from URI."); } break; default: hr = E_INVALIDARG; - ExitOnFailure(hr, "Unknown URI protocol."); + UriExitOnFailure(hr, "Unknown URI protocol."); } if (pProtocol) @@ -473,7 +488,7 @@ extern "C" HRESULT DAPI UriResolve( __in_z LPCWSTR wzUri, __in_opt LPCWSTR wzBaseUri, __out LPWSTR* ppwzResolvedUri, - __out_opt const URI_PROTOCOL* pResolvedProtocol + __out_opt URI_PROTOCOL* pResolvedProtocol ) { UNREFERENCED_PARAMETER(wzUri); @@ -486,45 +501,45 @@ extern "C" HRESULT DAPI UriResolve( URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; hr = UriProtocol(wzUri, &protocol); - ExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); + UriExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); - ExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); + UriExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); if (URI_PROTOCOL_UNKNOWN == protocol) { - ExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); + UriExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); if (L'/' == *wzUri || L'\\' == *wzUri) { hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol); - ExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); + UriExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); - ExitOnFailure(hr, "Failed to concat file to base URI."); + UriExitOnFailure(hr, "Failed to concat file to base URI."); } else { hr = UriProtocol(wzBaseUri, &protocol); - ExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); + UriExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); LPCWSTR pwcFile = const_cast (UriFile(wzBaseUri)); if (!pwcFile) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); + UriExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); } hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri); - ExitOnFailure(hr, "Failed to allocate string for resolved URI."); + UriExitOnFailure(hr, "Failed to allocate string for resolved URI."); hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); - ExitOnFailure(hr, "Failed to concat file to resolved URI."); + UriExitOnFailure(hr, "Failed to concat file to resolved URI."); } } else { hr = StrAllocString(ppwzResolvedUri, wzUri, 0); - ExitOnFailure(hr, "Failed to copy resolved URI."); + UriExitOnFailure(hr, "Failed to copy resolved URI."); } if (pResolvedProtocol) diff --git a/src/dutil/userutil.cpp b/src/dutil/userutil.cpp index 2e77f1df..ca6d5480 100644 --- a/src/dutil/userutil.cpp +++ b/src/dutil/userutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// UserExit macros +#define UserExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) +#define UserExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) +#define UserExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) +#define UserExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) +#define UserExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_USERUTIL, e, x, s, __VA_ARGS__) +#define UserExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_USERUTIL, g, x, s, __VA_ARGS__) + static BOOL CheckIsMemberHelper( __in_z LPCWSTR pwzGroupUserDomain, __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, @@ -29,14 +44,14 @@ extern "C" HRESULT DAPI UserBuildDomainUserName( if (cch >= cchLeft) { hr = ERROR_MORE_DATA; - ExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain); + UserExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain); } else if (cch > 0) { // handle the domain case hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0' - ExitOnFailure(hr, "Failed to copy Domain onto string."); + UserExitOnFailure(hr, "Failed to copy Domain onto string."); cchLeft -= cch; pwz += cch; @@ -45,11 +60,11 @@ extern "C" HRESULT DAPI UserBuildDomainUserName( if (1 >= cchLeft) { hr = ERROR_MORE_DATA; - ExitOnFailure(hr, "Insufficient buffer size while building domain user name"); + UserExitOnFailure(hr, "Insufficient buffer size while building domain user name"); } hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0' - ExitOnFailure(hr, "Failed to copy backslash onto string."); + UserExitOnFailure(hr, "Failed to copy backslash onto string."); --cchLeft; ++pwz; @@ -60,11 +75,11 @@ extern "C" HRESULT DAPI UserBuildDomainUserName( if (cch >= cchLeft) { hr = ERROR_MORE_DATA; - ExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName); + UserExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName); } hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0' - ExitOnFailure(hr, "Failed to copy User name onto string."); + UserExitOnFailure(hr, "Failed to copy User name onto string."); LExit: return hr; @@ -98,10 +113,10 @@ extern "C" HRESULT DAPI UserCheckIsMember( VARIANT_BOOL vtBoolResult = VARIANT_FALSE; hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain); - ExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain); - ExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); if (pwzDomain && *pwzDomain) { @@ -115,12 +130,12 @@ extern "C" HRESULT DAPI UserCheckIsMember( 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)); er = ERROR_SUCCESS; } - ExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName); + UserExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName); if (dwRead != dwTotal) { hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); - ExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName); + UserExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName); } if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) @@ -143,12 +158,12 @@ extern "C" HRESULT DAPI UserCheckIsMember( 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)); er = ERROR_SUCCESS; } - ExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName); + UserExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName); if (dwRead != dwTotal) { hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); - ExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName); + UserExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName); } if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) @@ -159,18 +174,18 @@ extern "C" HRESULT DAPI UserCheckIsMember( // If the above methods failed, let's try active directory hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser); - ExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain); + UserExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain); hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup); - ExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain); + UserExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain); if (lstrlenW(pwzGroupDomain) > 0) { hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - ExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); hr = pGroup->IsMember(bstrUser, &vtBoolResult); - ExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); } if (vtBoolResult) @@ -180,10 +195,10 @@ extern "C" HRESULT DAPI UserCheckIsMember( } hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - ExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); hr = pGroup->IsMember(bstrUser, &vtBoolResult); - ExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); if (vtBoolResult) { @@ -222,25 +237,25 @@ extern "C" HRESULT DAPI UserCreateADsPath( LPWSTR pwzAdsPath = NULL; hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0); - ExitOnFailure(hr, "failed to allocate AdsPath string"); + UserExitOnFailure(hr, "failed to allocate AdsPath string"); if (*wzObjectDomain) { hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName); - ExitOnFailure(hr, "failed to allocate AdsPath string"); + UserExitOnFailure(hr, "failed to allocate AdsPath string"); } else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/")) { hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); - ExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName); + UserExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName); } else { hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0); - ExitOnFailure(hr, "failed to concat LocalHost/"); + UserExitOnFailure(hr, "failed to concat LocalHost/"); hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); - ExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName); + UserExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName); } *pbstrAdsPath = ::SysAllocString(pwzAdsPath); diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp index 7336d685..ffbfe85a 100644 --- a/src/dutil/wiutil.cpp +++ b/src/dutil/wiutil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) +#define WiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) +#define WiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) +#define WiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) +#define WiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WIUTIL, e, x, s, __VA_ARGS__) +#define WiuExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WIUTIL, g, x, s, __VA_ARGS__) + + // constants const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF; @@ -112,8 +127,8 @@ static DWORD CalculatePhaseProgress( __in DWORD dwWeightPercentage ); void InitializeMessageData( - __in MSIHANDLE hRecord, - __out LPWSTR** prgsczData, + __in_opt MSIHANDLE hRecord, + __deref_out_ecount(*pcData) LPWSTR** prgsczData, __out DWORD* pcData ); void UninitializeMessageData( @@ -133,7 +148,7 @@ extern "C" HRESULT DAPI WiuInitialize( LPWSTR sczMsiDllPath = NULL; hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath); - ExitOnFailure(hr, "Failed to load Msi.DLL"); + WiuExitOnFailure(hr, "Failed to load Msi.DLL"); // Ignore failures FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision); @@ -275,7 +290,7 @@ extern "C" HRESULT DAPI WiuGetComponentPath( DWORD cchCompare; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string for component path."); + WiuExitOnFailure(hr, "Failed to allocate string for component path."); cchCompare = cch; *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); @@ -283,7 +298,7 @@ extern "C" HRESULT DAPI WiuGetComponentPath( { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for component path."); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); cchCompare = cch; *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); @@ -292,7 +307,7 @@ extern "C" HRESULT DAPI WiuGetComponentPath( if (INSTALLSTATE_INVALIDARG == *pInstallState) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid argument when getting component path."); + WiuExitOnRootFailure(hr, "Invalid argument when getting component path."); } else if (INSTALLSTATE_UNKNOWN == *pInstallState) { @@ -306,7 +321,7 @@ extern "C" HRESULT DAPI WiuGetComponentPath( { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for component path."); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); } @@ -327,7 +342,7 @@ extern "C" HRESULT DAPI WiuLocateComponent( DWORD cchCompare; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string for component path."); + WiuExitOnFailure(hr, "Failed to allocate string for component path."); cchCompare = cch; *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); @@ -335,7 +350,7 @@ extern "C" HRESULT DAPI WiuLocateComponent( { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for component path."); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); cchCompare = cch; *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); @@ -344,7 +359,7 @@ extern "C" HRESULT DAPI WiuLocateComponent( if (INSTALLSTATE_INVALIDARG == *pInstallState) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid argument when locating component."); + WiuExitOnRootFailure(hr, "Invalid argument when locating component."); } else if (INSTALLSTATE_UNKNOWN == *pInstallState) { @@ -358,7 +373,7 @@ extern "C" HRESULT DAPI WiuLocateComponent( { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for component path."); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); } @@ -380,7 +395,7 @@ extern "C" HRESULT DAPI WiuQueryFeatureState( if (INSTALLSTATE_INVALIDARG == *pInstallState) { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); + WiuExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); } LExit: @@ -399,18 +414,18 @@ extern "C" HRESULT DAPI WiuGetProductInfo( DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string for product info."); + WiuExitOnFailure(hr, "Failed to allocate string for product info."); er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); if (ERROR_MORE_DATA == er) { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for product info."); + WiuExitOnFailure(hr, "Failed to reallocate string for product info."); er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); } - ExitOnWin32Error(er, hr, "Failed to get product info."); + WiuExitOnWin32Error(er, hr, "Failed to get product info."); LExit: return hr; @@ -432,24 +447,24 @@ extern "C" HRESULT DAPI WiuGetProductInfoEx( if (!vpfnMsiGetProductInfoExW) { hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue); - ExitOnFailure(hr, "Failed to get product info when extended info was not available."); + WiuExitOnFailure(hr, "Failed to get product info when extended info was not available."); ExitFunction(); } hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string for extended product info."); + WiuExitOnFailure(hr, "Failed to allocate string for extended product info."); er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); if (ERROR_MORE_DATA == er) { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for extended product info."); + WiuExitOnFailure(hr, "Failed to reallocate string for extended product info."); er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); } - ExitOnWin32Error(er, hr, "Failed to get extended product info."); + WiuExitOnWin32Error(er, hr, "Failed to get extended product info."); LExit: return hr; @@ -467,18 +482,18 @@ extern "C" HRESULT DAPI WiuGetProductProperty( DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string for product property."); + WiuExitOnFailure(hr, "Failed to allocate string for product property."); er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); if (ERROR_MORE_DATA == er) { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for product property."); + WiuExitOnFailure(hr, "Failed to reallocate string for product property."); er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); } - ExitOnWin32Error(er, hr, "Failed to get product property."); + WiuExitOnWin32Error(er, hr, "Failed to get product property."); LExit: return hr; @@ -504,18 +519,18 @@ extern "C" HRESULT DAPI WiuGetPatchInfoEx( } hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to allocate string for extended patch info."); + WiuExitOnFailure(hr, "Failed to allocate string for extended patch info."); er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); if (ERROR_MORE_DATA == er) { ++cch; hr = StrAlloc(psczValue, cch); - ExitOnFailure(hr, "Failed to reallocate string for extended patch info."); + WiuExitOnFailure(hr, "Failed to reallocate string for extended patch info."); er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); } - ExitOnWin32Error(er, hr, "Failed to get extended patch info."); + WiuExitOnWin32Error(er, hr, "Failed to get extended patch info."); LExit: return hr; @@ -539,7 +554,7 @@ extern "C" HRESULT DAPI WiuDeterminePatchSequence( } er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo); - ExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); + WiuExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); LExit: return hr; @@ -561,7 +576,7 @@ extern "C" HRESULT DAPI WiuDetermineApplicablePatches( } er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); - ExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); + WiuExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); LExit: return hr; @@ -581,7 +596,7 @@ extern "C" HRESULT DAPI WiuEnumProducts( { ExitFunction1(hr = HRESULT_FROM_WIN32(er)); } - ExitOnWin32Error(er, hr, "Failed to enumerate products."); + WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); LExit: return hr; @@ -612,7 +627,7 @@ extern "C" HRESULT DAPI WiuEnumProductsEx( { ExitFunction1(hr = HRESULT_FROM_WIN32(er)); } - ExitOnWin32Error(er, hr, "Failed to enumerate products."); + WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); LExit: return hr; @@ -633,7 +648,7 @@ extern "C" HRESULT DAPI WiuEnumRelatedProducts( { ExitFunction1(hr = HRESULT_FROM_WIN32(er)); } - ExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); + WiuExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); LExit: return hr; @@ -650,7 +665,7 @@ LExit: ********************************************************************/ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( __in_z LPCWSTR wzUpgradeCode, - __deref_out_ecount_opt(pcRelatedProducts) LPWSTR** prgsczProductCodes, + __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, __out DWORD* pcRelatedProducts, __in BOOL fReturnHighestVersionOnly ) @@ -673,16 +688,16 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( hr = S_OK; break; } - ExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); + WiuExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); if (fReturnHighestVersionOnly) { // get the version hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); - ExitOnFailure(hr, "Failed to get version for product code: %ls", wzCurrentProductCode); + WiuExitOnFailure(hr, "Failed to get version for product code: %ls", wzCurrentProductCode); hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwCurrentVersion); - ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for product code: %ls", sczInstalledVersion, wzCurrentProductCode); + WiuExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for product code: %ls", sczInstalledVersion, wzCurrentProductCode); // if this is the first product found then it is the highest version (for now) if (0 == *pcRelatedProducts) @@ -698,7 +713,7 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( qwHighestVersion = qwCurrentVersion; hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); - ExitOnFailure(hr, "Failed to update array with higher versioned product code."); + WiuExitOnFailure(hr, "Failed to update array with higher versioned product code."); } // continue here as we don't want anything else added to the list @@ -707,7 +722,7 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( } hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0); - ExitOnFailure(hr, "Failed to add product code to array."); + WiuExitOnFailure(hr, "Failed to add product code to array."); } LExit: @@ -726,7 +741,7 @@ extern "C" HRESULT DAPI WiuEnableLog( DWORD er = ERROR_SUCCESS; er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes); - ExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); + WiuExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); LExit: return hr; @@ -780,7 +795,7 @@ extern "C" HRESULT DAPI WiuInitializeExternalUI( // Wire the internal and external UI handler. hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext); - ExitOnFailure(hr, "Failed to set internal UI level and window."); + WiuExitOnFailure(hr, "Failed to set internal UI level and window."); pExecuteContext->fRollback = fRollback; pExecuteContext->pfnMessageHandler = pfnMessageHandler; @@ -791,7 +806,7 @@ extern "C" HRESULT DAPI WiuInitializeExternalUI( if (vpfnMsiSetExternalUIRecord) { er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord); - ExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); + WiuExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); pExecuteContext->fSetPreviousExternalUIRecord = TRUE; } else @@ -841,7 +856,7 @@ extern "C" HRESULT DAPI WiuConfigureProductEx( er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine); er = CheckForRestartErrorCode(er, pRestart); - ExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); + WiuExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); LExit: return hr; @@ -859,7 +874,7 @@ extern "C" HRESULT DAPI WiuInstallProduct( er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine); er = CheckForRestartErrorCode(er, pRestart); - ExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); + WiuExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); LExit: return hr; @@ -878,7 +893,7 @@ extern "C" HRESULT DAPI WiuRemovePatches( er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList); er = CheckForRestartErrorCode(er, pRestart); - ExitOnWin32Error(er, hr, "Failed to remove patches."); + WiuExitOnWin32Error(er, hr, "Failed to remove patches."); LExit: return hr; @@ -898,7 +913,7 @@ extern "C" HRESULT DAPI WiuSourceListAddSourceEx( DWORD er = ERROR_SUCCESS; er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex); - ExitOnWin32Error(er, hr, "Failed to add source."); + WiuExitOnWin32Error(er, hr, "Failed to add source."); LExit: return hr; @@ -924,14 +939,14 @@ extern "C" HRESULT DAPI WiuBeginTransaction( if (!WiuIsMsiTransactionSupported()) { - ExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); } hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); - ExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent); - ExitOnWin32Error(er, hr, "Failed to begin transaction."); + WiuExitOnWin32Error(er, hr, "Failed to begin transaction."); LExit: return hr; @@ -948,14 +963,14 @@ extern "C" HRESULT DAPI WiuEndTransaction( if (!WiuIsMsiTransactionSupported()) { - ExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); } hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); - ExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); er = vpfnMsiEndTransaction(dwTransactionState); - ExitOnWin32Error(er, hr, "Failed to end transaction."); + WiuExitOnWin32Error(er, hr, "Failed to end transaction."); LExit: return hr; @@ -1048,10 +1063,10 @@ static INT CALLBACK InstallEngineRecordCallback( { hr = HRESULT_FROM_WIN32(er); } - ExitOnFailure(hr, "Failed to allocate string for formated message."); + WiuExitOnFailure(hr, "Failed to allocate string for formated message."); er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage); - ExitOnWin32Error(er, hr, "Failed to format message record."); + WiuExitOnWin32Error(er, hr, "Failed to format message record."); // Pass to handler including both the formated message and the original record. nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord); @@ -1213,7 +1228,7 @@ static INT HandleInstallProgress( // parse number hr = StrStringToInt32(pwz, cch, &iFields[cFields]); - ExitOnFailure(hr, "Failed to parse MSI message part."); + WiuExitOnFailure(hr, "Failed to parse MSI message part."); // increment field count ++cFields; @@ -1255,7 +1270,7 @@ static INT HandleInstallProgress( else { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - ExitOnRootFailure(hr, "Insufficient space to hold progress information."); + WiuExitOnRootFailure(hr, "Insufficient space to hold progress information."); } // we only care about the first stage after script execution has started diff --git a/src/dutil/wuautil.cpp b/src/dutil/wuautil.cpp index 94ab659d..dfb28818 100644 --- a/src/dutil/wuautil.cpp +++ b/src/dutil/wuautil.cpp @@ -3,6 +3,21 @@ #include "precomp.h" +// Exit macros +#define WuaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) +#define WuaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) +#define WuaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) +#define WuaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) +#define WuaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WUAUTIL, e, x, s, __VA_ARGS__) +#define WuaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WUAUTIL, g, x, s, __VA_ARGS__) + + // internal function declarations static HRESULT GetAutomaticUpdatesService( @@ -18,10 +33,10 @@ extern "C" HRESULT DAPI WuaPauseAutomaticUpdates() IAutomaticUpdates *pAutomaticUpdates = NULL; hr = GetAutomaticUpdatesService(&pAutomaticUpdates); - ExitOnFailure(hr, "Failed to get the Automatic Updates service."); + WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); hr = pAutomaticUpdates->Pause(); - ExitOnFailure(hr, "Failed to pause the Automatic Updates service."); + WuaExitOnFailure(hr, "Failed to pause the Automatic Updates service."); LExit: ReleaseObject(pAutomaticUpdates); @@ -35,10 +50,10 @@ extern "C" HRESULT DAPI WuaResumeAutomaticUpdates() IAutomaticUpdates *pAutomaticUpdates = NULL; hr = GetAutomaticUpdatesService(&pAutomaticUpdates); - ExitOnFailure(hr, "Failed to get the Automatic Updates service."); + WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); hr = pAutomaticUpdates->Resume(); - ExitOnFailure(hr, "Failed to resume the Automatic Updates service."); + WuaExitOnFailure(hr, "Failed to resume the Automatic Updates service."); LExit: ReleaseObject(pAutomaticUpdates); @@ -55,10 +70,10 @@ extern "C" HRESULT DAPI WuaRestartRequired( VARIANT_BOOL bRestartRequired; hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast(&pSystemInformation)); - ExitOnRootFailure(hr, "Failed to get WUA system information interface."); + WuaExitOnRootFailure(hr, "Failed to get WUA system information interface."); hr = pSystemInformation->get_RebootRequired(&bRestartRequired); - ExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); + WuaExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); *pfRestartRequired = (VARIANT_FALSE != bRestartRequired); @@ -79,10 +94,10 @@ static HRESULT GetAutomaticUpdatesService( CLSID clsidAutomaticUpdates = { }; hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates); - ExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate."); + WuaExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate."); hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast(ppAutomaticUpdates)); - ExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate."); + WuaExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate."); LExit: return hr; diff --git a/src/dutil/xmlutil.cpp b/src/dutil/xmlutil.cpp index f97ca962..6ecd2449 100644 --- a/src/dutil/xmlutil.cpp +++ b/src/dutil/xmlutil.cpp @@ -2,6 +2,21 @@ #include "precomp.h" + +// Exit macros +#define XmlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) +#define XmlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) +#define XmlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) +#define XmlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) +#define XmlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_XMLUTIL, e, x, s, __VA_ARGS__) +#define XmlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_XMLUTIL, g, x, s, __VA_ARGS__) + // intialization globals CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} }; static volatile LONG vcXmlInitialized = 0; @@ -23,7 +38,7 @@ extern "C" HRESULT DAPI XmlInitialize( hr = ::CoInitialize(0); if (RPC_E_CHANGED_MODE != hr) { - ExitOnFailure(hr, "failed to initialize COM"); + XmlExitOnFailure(hr, "failed to initialize COM"); fComInitialized = TRUE; } } @@ -47,7 +62,7 @@ extern "C" HRESULT DAPI XmlInitialize( // try to fall back to old MSXML hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM); } - ExitOnFailure(hr, "failed to get CLSID for XML DOM"); + XmlExitOnFailure(hr, "failed to get CLSID for XML DOM"); Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) || @@ -99,7 +114,7 @@ extern "C" HRESULT DAPI XmlCreateElement( HRESULT hr = S_OK; BSTR bstrElementName = ::SysAllocString(wzElementName); - ExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + XmlExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString"); hr = pixdDocument->createElement(bstrElementName, ppixnElement); LExit: ReleaseBSTR(bstrElementName); @@ -130,7 +145,7 @@ extern "C" HRESULT DAPI XmlCreateDocument( // Test if we have access to the Wow64 API, and store the result in fWow64Available HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll"); - ExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll"); + XmlExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll"); // This will test if we have access to the Wow64 API if (NULL != GetProcAddress(hKernel32, "IsWow64Process")) @@ -155,7 +170,7 @@ extern "C" HRESULT DAPI XmlCreateDocument( } hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument); - ExitOnFailure(hr, "failed to create XML DOM Document"); + XmlExitOnFailure(hr, "failed to create XML DOM Document"); Assert(pixdDocument); if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20)) @@ -166,9 +181,9 @@ extern "C" HRESULT DAPI XmlCreateDocument( if (pwzElementName) { hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement); - ExitOnFailure(hr, "failed XmlCreateElement"); + XmlExitOnFailure(hr, "failed XmlCreateElement"); hr = pixdDocument->appendChild(pixeRootElement, NULL); - ExitOnFailure(hr, "failed appendChild"); + XmlExitOnFailure(hr, "failed appendChild"); } *ppixdDocument = pixdDocument; @@ -222,28 +237,28 @@ static void XmlReportParseError( Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:"); hr = pixpe->get_errorCode(&lNumber); - ExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode."); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode."); Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber); hr = pixpe->get_filepos(&lNumber); - ExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos."); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos."); Trace(REPORT_STANDARD, "filepos = %d", lNumber); hr = pixpe->get_line(&lNumber); - ExitOnFailure(hr, "Failed to query IXMLDOMParseError.line."); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.line."); Trace(REPORT_STANDARD, "line = %d", lNumber); hr = pixpe->get_linepos(&lNumber); - ExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos."); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos."); Trace(REPORT_STANDARD, "linepos = %d", lNumber); hr = pixpe->get_reason(&bstr); - ExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason."); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason."); Trace(REPORT_STANDARD, "reason = %ls", bstr); ReleaseNullBSTR(bstr); hr = pixpe->get_srcText (&bstr); - ExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText ."); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText ."); Trace(REPORT_STANDARD, "srcText = %ls", bstr); ReleaseNullBSTR(bstr); @@ -272,7 +287,7 @@ extern "C" HRESULT DAPI XmlLoadDocumentEx( if (!wzDocument || !*wzDocument) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "string must be non-null"); + XmlExitOnFailure(hr, "string must be non-null"); } hr = XmlCreateDocument(NULL, &pixd); @@ -280,22 +295,22 @@ extern "C" HRESULT DAPI XmlLoadDocumentEx( { hr = E_FAIL; } - ExitOnFailure(hr, "failed XmlCreateDocument"); + XmlExitOnFailure(hr, "failed XmlCreateDocument"); if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) { hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); - ExitOnFailure(hr, "failed put_preserveWhiteSpace"); + XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); } // Security issue. Avoid triggering anything external. hr = pixd->put_validateOnParse(VARIANT_FALSE); - ExitOnFailure(hr, "failed put_validateOnParse"); + XmlExitOnFailure(hr, "failed put_validateOnParse"); hr = pixd->put_resolveExternals(VARIANT_FALSE); - ExitOnFailure(hr, "failed put_resolveExternals"); + XmlExitOnFailure(hr, "failed put_resolveExternals"); bstrLoad = ::SysAllocString(wzDocument); - ExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx"); + XmlExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx"); hr = pixd->loadXML(bstrLoad, &vbSuccess); if (S_FALSE == hr) @@ -308,7 +323,7 @@ extern "C" HRESULT DAPI XmlLoadDocumentEx( XmlReportParseError(pixpe); } - ExitOnFailure(hr, "failed loadXML"); + XmlExitOnFailure(hr, "failed loadXML"); hr = S_OK; @@ -359,26 +374,26 @@ extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx( ::VariantInit(&varPath); varPath.vt = VT_BSTR; varPath.bstrVal = ::SysAllocString(wzPath); - ExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx"); + XmlExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx"); hr = XmlCreateDocument(NULL, &pixd); if (hr == S_FALSE) { hr = E_FAIL; } - ExitOnFailure(hr, "failed XmlCreateDocument"); + XmlExitOnFailure(hr, "failed XmlCreateDocument"); if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) { hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); - ExitOnFailure(hr, "failed put_preserveWhiteSpace"); + XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); } // Avoid triggering anything external. hr = pixd->put_validateOnParse(VARIANT_FALSE); - ExitOnFailure(hr, "failed put_validateOnParse"); + XmlExitOnFailure(hr, "failed put_validateOnParse"); hr = pixd->put_resolveExternals(VARIANT_FALSE); - ExitOnFailure(hr, "failed put_resolveExternals"); + XmlExitOnFailure(hr, "failed put_resolveExternals"); pixd->put_async(VARIANT_FALSE); hr = pixd->load(varPath, &vbSuccess); @@ -392,7 +407,7 @@ extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx( XmlReportParseError(pixpe); } - ExitOnFailure(hr, "failed to load XML from: %ls", wzPath); + XmlExitOnFailure(hr, "failed to load XML from: %ls", wzPath); if (ppixdDocument) { @@ -434,13 +449,13 @@ extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( { hr = E_FAIL; } - ExitOnFailure(hr, "failed XmlCreateDocument"); + XmlExitOnFailure(hr, "failed XmlCreateDocument"); // Security issue. Avoid triggering anything external. hr = pixdDocument->put_validateOnParse(VARIANT_FALSE); - ExitOnFailure(hr, "failed put_validateOnParse"); + XmlExitOnFailure(hr, "failed put_validateOnParse"); hr = pixdDocument->put_resolveExternals(VARIANT_FALSE); - ExitOnFailure(hr, "failed put_resolveExternals"); + XmlExitOnFailure(hr, "failed put_resolveExternals"); // load document sa.cDims = 1; @@ -456,7 +471,7 @@ extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( { hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); } - ExitOnFailure(hr, "failed loadXML"); + XmlExitOnFailure(hr, "failed loadXML"); // return value *ppixdDocument = pixdDocument; @@ -488,20 +503,20 @@ extern "C" HRESULT DAPI XmlSetAttribute( IXMLDOMAttribute* pixaAttribute = NULL; IXMLDOMNode* pixaNode = NULL; BSTR bstrAttributeName = ::SysAllocString(pwzAttribute); - ExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute"); + XmlExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute"); hr = pixnNode->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute); hr = pixnNode->get_ownerDocument(&pixdDocument); if (hr == S_FALSE) { hr = E_FAIL; } - ExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute); - ExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute); varAttributeValue.vt = VT_BSTR; varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue); @@ -509,13 +524,13 @@ extern "C" HRESULT DAPI XmlSetAttribute( { hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); } - ExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute"); + XmlExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute"); hr = pixaAttribute->put_nodeValue(varAttributeValue); - ExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute); hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode); - ExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute); LExit: ReleaseObject(pixdDocument); @@ -543,11 +558,11 @@ extern "C" HRESULT DAPI XmlSelectSingleNode( BSTR bstrXPath = NULL; - ExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode"); - ExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode"); + XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode"); + XmlExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode"); bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); - ExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode"); + XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode"); hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild); @@ -575,7 +590,7 @@ extern "C" HRESULT DAPI XmlCreateTextNode( HRESULT hr = S_OK; BSTR bstrText = ::SysAllocString(wzText); - ExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString"); + XmlExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString"); hr = pixdDocument->createTextNode(bstrText, ppixnTextNode); LExit: ReleaseBSTR(bstrText); @@ -621,7 +636,7 @@ extern "C" HRESULT DAPI XmlGetAttribute( // get attribute value from source hr = pixnNode->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "failed get_attributes"); + XmlExitOnFailure(hr, "failed get_attributes"); hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); if (S_FALSE == hr) @@ -629,10 +644,10 @@ extern "C" HRESULT DAPI XmlGetAttribute( // hr = E_FAIL; ExitFunction(); } - ExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute); hr = pixnAttribute->get_nodeValue(&varAttributeValue); - ExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute); // steal the BSTR from the VARIANT if (S_OK == hr && pbstrAttributeValue) @@ -672,28 +687,28 @@ HRESULT DAPI XmlGetAttributeEx( // get attribute value from source hr = pixnNode->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "Failed get_attributes."); + XmlExitOnFailure(hr, "Failed get_attributes."); bstrAttribute = ::SysAllocString(wzAttribute); - ExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR."); + XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR."); hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); if (S_FALSE == hr) { ExitFunction1(hr = E_NOTFOUND); } - ExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute); + XmlExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute); hr = pixnAttribute->get_nodeValue(&varAttributeValue); if (S_FALSE == hr) { ExitFunction1(hr = E_NOTFOUND); } - ExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute); + XmlExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute); // copy value hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0); - ExitOnFailure(hr, "Failed to copy attribute value."); + XmlExitOnFailure(hr, "Failed to copy attribute value."); LExit: ReleaseObject(pixnnmAttributes); @@ -721,7 +736,7 @@ HRESULT DAPI XmlGetYesNoAttribute( hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue); if (E_NOTFOUND != hr) { - ExitOnFailure(hr, "Failed to get attribute."); + XmlExitOnFailure(hr, "Failed to get attribute."); *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1); } @@ -764,7 +779,7 @@ extern "C" HRESULT DAPI XmlGetAttributeNumberBase( BSTR bstrPointer = NULL; hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer); - ExitOnFailure(hr, "Failed to get value from attribute."); + XmlExitOnFailure(hr, "Failed to get value from attribute."); if (S_OK == hr) { @@ -791,13 +806,13 @@ extern "C" HRESULT DAPI XmlGetAttributeLargeNumber( BSTR bstrValue = NULL; hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue); - ExitOnFailure(hr, "failed XmlGetAttribute"); + XmlExitOnFailure(hr, "failed XmlGetAttribute"); if (S_OK == hr) { LONGLONG ll = 0; hr = StrStringToInt64(bstrValue, 0, &ll); - ExitOnFailure(hr, "Failed to treat attribute value as number."); + XmlExitOnFailure(hr, "Failed to treat attribute value as number."); *pdw64Value = ll; } @@ -829,7 +844,7 @@ extern "C" HRESULT DAPI XmlGetNamedItem( HRESULT hr = S_OK; BSTR bstrName = ::SysAllocString(wzName); - ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + XmlExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString"); hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem); @@ -863,12 +878,12 @@ extern "C" HRESULT DAPI XmlSetText( // find the text node hr = pixnNode->get_childNodes(&pixnlNodeList); - ExitOnFailure(hr, "failed to get child nodes"); + XmlExitOnFailure(hr, "failed to get child nodes"); while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode))) { hr = pixnChildNode->get_nodeType(&dnType); - ExitOnFailure(hr, "failed to get node type"); + XmlExitOnFailure(hr, "failed to get node type"); if (NODE_TEXT == dnType) break; @@ -887,10 +902,10 @@ extern "C" HRESULT DAPI XmlSetText( { hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); } - ExitOnFailure(hr, "failed SysAllocString in XmlSetText"); + XmlExitOnFailure(hr, "failed SysAllocString in XmlSetText"); hr = pixnChildNode->put_nodeValue(varText); - ExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue"); + XmlExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue"); } else { @@ -899,13 +914,13 @@ extern "C" HRESULT DAPI XmlSetText( { hr = E_FAIL; } - ExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode); - ExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText); + XmlExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText); hr = pixnNode->appendChild(pixtTextNode, NULL); - ExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText); + XmlExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText); } hr = *pwzText ? S_OK : S_FALSE; @@ -933,7 +948,7 @@ extern "C" HRESULT DAPI XmlSetTextNumber( WCHAR wzValue[12]; hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue); - ExitOnFailure(hr, "Failed to format numeric value as string."); + XmlExitOnFailure(hr, "Failed to format numeric value as string."); hr = XmlSetText(pixnNode, wzValue); @@ -963,21 +978,21 @@ extern "C" HRESULT DAPI XmlCreateChild( { hr = E_FAIL; } - ExitOnFailure(hr, "failed get_ownerDocument"); + XmlExitOnFailure(hr, "failed get_ownerDocument"); hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild); if (hr == S_FALSE) { hr = E_FAIL; } - ExitOnFailure(hr, "failed createElement"); + XmlExitOnFailure(hr, "failed createElement"); pixnParent->appendChild(pixnChild,NULL); if (hr == S_FALSE) { hr = E_FAIL; } - ExitOnFailure(hr, "failed appendChild"); + XmlExitOnFailure(hr, "failed appendChild"); if (ppixnChild) { @@ -1005,13 +1020,13 @@ extern "C" HRESULT DAPI XmlRemoveAttribute( // RELEASEME IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; BSTR bstrAttribute = ::SysAllocString(pwzAttribute); - ExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute"); + XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute"); hr = pixnNode->get_attributes(&pixnnmAttributes); - ExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute); hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL); - ExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute); + XmlExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute); LExit: ReleaseObject(pixnnmAttributes); @@ -1035,11 +1050,11 @@ extern "C" HRESULT DAPI XmlSelectNodes( BSTR bstrXPath = NULL; - ExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes"); - ExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes"); + XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes"); + XmlExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes"); bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); - ExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes"); + XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes"); hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren); @@ -1077,24 +1092,24 @@ extern "C" HRESULT DAPI XmlNextAttribute( } hr = pixnnm->nextNode(&pixn); - ExitOnFailure(hr, "Failed to get next attribute."); + XmlExitOnFailure(hr, "Failed to get next attribute."); if (S_OK == hr) { hr = pixn->get_nodeType(&nt); - ExitOnFailure(hr, "failed to get node type"); + XmlExitOnFailure(hr, "failed to get node type"); if (NODE_ATTRIBUTE != nt) { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Failed to get expected node type back: attribute"); + XmlExitOnFailure(hr, "Failed to get expected node type back: attribute"); } // if the caller asked for the attribute name if (pbstrAttribute) { hr = pixn->get_baseName(pbstrAttribute); - ExitOnFailure(hr, "failed to get attribute name"); + XmlExitOnFailure(hr, "failed to get attribute name"); } *pixnAttribute = pixn; @@ -1140,20 +1155,20 @@ extern "C" HRESULT DAPI XmlNextElement( while (S_OK == (hr = pixnl->nextNode(&pixn))) { hr = pixn->get_nodeType(&nt); - ExitOnFailure(hr, "failed to get node type"); + XmlExitOnFailure(hr, "failed to get node type"); if (NODE_ELEMENT == nt) break; ReleaseNullObject(pixn); } - ExitOnFailure(hr, "failed to get next element"); + XmlExitOnFailure(hr, "failed to get next element"); // if we have a node and the caller asked for the element name if (pixn && pbstrElement) { hr = pixn->get_baseName(pbstrElement); - ExitOnFailure(hr, "failed to get element name"); + XmlExitOnFailure(hr, "failed to get element name"); } *pixnElement = pixn; @@ -1185,12 +1200,12 @@ extern "C" HRESULT DAPI XmlRemoveChildren( if (pwzXPath) { hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList); - ExitOnFailure(hr, "failed XmlSelectNodes"); + XmlExitOnFailure(hr, "failed XmlSelectNodes"); } else { hr = pixnSource->get_childNodes(&pixnlNodeList); - ExitOnFailure(hr, "failed childNodes"); + XmlExitOnFailure(hr, "failed childNodes"); } if (S_FALSE == hr) { @@ -1200,7 +1215,7 @@ extern "C" HRESULT DAPI XmlRemoveChildren( while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode))) { hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild); - ExitOnFailure(hr, "failed removeChild"); + XmlExitOnFailure(hr, "failed removeChild"); ReleaseNullObject(pixnRemoveChild); ReleaseNullObject(pixnNode); @@ -1240,14 +1255,14 @@ extern "C" HRESULT DAPI XmlSaveDocument( { hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); } - ExitOnFailure(hr, "failed to create BSTR"); + XmlExitOnFailure(hr, "failed to create BSTR"); hr = pixdDocument->save(varsDestPath); if (hr == S_FALSE) { hr = E_FAIL; } - ExitOnFailure(hr, "failed save in WriteDocument"); + XmlExitOnFailure(hr, "failed save in WriteDocument"); LExit: ReleaseVariant(varsDestPath); @@ -1277,33 +1292,33 @@ extern "C" HRESULT DAPI XmlSaveDocumentToBuffer( // create stream hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream); - ExitOnFailure(hr, "Failed to create stream."); + XmlExitOnFailure(hr, "Failed to create stream."); // write document to stream vtDestination.vt = VT_UNKNOWN; vtDestination.punkVal = (IUnknown*)pStream; hr = pixdDocument->save(vtDestination); - ExitOnFailure(hr, "Failed to save document."); + XmlExitOnFailure(hr, "Failed to save document."); // get stream size hr = pStream->Stat(&statstg, STATFLAG_NONAME); - ExitOnFailure(hr, "Failed to get stream size."); + XmlExitOnFailure(hr, "Failed to get stream size."); // allocate buffer pbDest = static_cast(MemAlloc((SIZE_T)statstg.cbSize.LowPart, TRUE)); - ExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); + XmlExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); // read data from stream li.QuadPart = 0; hr = pStream->Seek(li, STREAM_SEEK_SET, NULL); - ExitOnFailure(hr, "Failed to seek stream."); + XmlExitOnFailure(hr, "Failed to seek stream."); hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead); if (cbRead < statstg.cbSize.LowPart) { hr = E_FAIL; } - ExitOnFailure(hr, "Failed to read stream content to buffer."); + XmlExitOnFailure(hr, "Failed to read stream content to buffer."); // return value *ppbDest = pbDest; -- cgit v1.2.3-55-g6feb From ed0ef472c76ac0d2a3d7a138e4f3b7ad950a56bc Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 30 Mar 2021 17:15:25 -0500 Subject: Add test for apuputil and fix descending sorting. --- src/dutil/apuputil.cpp | 27 +++++---- src/dutil/inc/verutil.h | 4 +- src/dutil/verutil.cpp | 16 +++-- src/test/DUtilUnitTest/ApupUtilTests.cpp | 46 +++++++++++++++ src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 4 ++ .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 3 + .../TestData/ApupUtilTests/FeedBv2.0.xml | 68 ++++++++++++++++++++++ src/test/DUtilUnitTest/precomp.h | 6 +- 8 files changed, 155 insertions(+), 19 deletions(-) create mode 100644 src/test/DUtilUnitTest/ApupUtilTests.cpp create mode 100644 src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp index 07d591a7..6f5825bb 100644 --- a/src/dutil/apuputil.cpp +++ b/src/dutil/apuputil.cpp @@ -208,7 +208,6 @@ static HRESULT ProcessEntry( ) { HRESULT hr = S_OK; - BOOL fVersionFound = FALSE; int nCompareResult = 0; // First search the ATOM entry's custom elements to try and find the application update information. @@ -255,25 +254,26 @@ static HRESULT ProcessEntry( { hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion); ApupExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); - - fVersionFound = TRUE; } } } // If there is no application identity or no version, skip the whole thing. - if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !fVersionFound) + if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !pApupEntry->pVersion) { ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version. } - hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare version to upgrade version."); - - if (nCompareResult >= 0) + if (pApupEntry->pUpgradeVersion) { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); + hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare version to upgrade version."); + + if (nCompareResult >= 0) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); + } } if (pAtomEntry->wzTitle) @@ -443,11 +443,14 @@ static __callback int __cdecl CompareEntries( VerCompareParsedVersions(pEntryLeft->pUpgradeVersion, pEntryRight->pUpgradeVersion, &ret); if (0 == ret) { - ret = (pEntryRight->dw64TotalSize < pEntryLeft->dw64TotalSize) ? -1 : - (pEntryRight->dw64TotalSize > pEntryLeft->dw64TotalSize) ? 1 : 0; + ret = (pEntryLeft->dw64TotalSize < pEntryRight->dw64TotalSize) ? -1 : + (pEntryLeft->dw64TotalSize > pEntryRight->dw64TotalSize) ? 1 : 0; } } + // Sort descending. + ret = -ret; + return ret; } diff --git a/src/dutil/inc/verutil.h b/src/dutil/inc/verutil.h index 30869aef..3caa17e1 100644 --- a/src/dutil/inc/verutil.h +++ b/src/dutil/inc/verutil.h @@ -34,8 +34,8 @@ typedef struct _VERUTIL_VERSION *******************************************************************/ HRESULT DAPI VerCompareParsedVersions( - __in VERUTIL_VERSION* pVersion1, - __in VERUTIL_VERSION* pVersion2, + __in_opt VERUTIL_VERSION* pVersion1, + __in_opt VERUTIL_VERSION* pVersion2, __out int* pnResult ); diff --git a/src/dutil/verutil.cpp b/src/dutil/verutil.cpp index 835dde81..fdb5a10a 100644 --- a/src/dutil/verutil.cpp +++ b/src/dutil/verutil.cpp @@ -40,8 +40,8 @@ static HRESULT CompareVersionSubstring( DAPI_(HRESULT) VerCompareParsedVersions( - __in VERUTIL_VERSION* pVersion1, - __in VERUTIL_VERSION* pVersion2, + __in_opt VERUTIL_VERSION* pVersion1, + __in_opt VERUTIL_VERSION* pVersion2, __out int* pnResult ) { @@ -50,8 +50,8 @@ DAPI_(HRESULT) VerCompareParsedVersions( DWORD cMaxReleaseLabels = 0; BOOL fCompareMetadata = FALSE; - if (!pVersion1 || !pVersion1->sczVersion || - !pVersion2 || !pVersion2->sczVersion) + if (pVersion1 && !pVersion1->sczVersion || + pVersion2 && !pVersion2->sczVersion) { ExitFunction1(hr = E_INVALIDARG); } @@ -60,6 +60,14 @@ DAPI_(HRESULT) VerCompareParsedVersions( { ExitFunction1(nResult = 0); } + else if (pVersion1 && !pVersion2) + { + ExitFunction1(nResult = 1); + } + else if (!pVersion1 && pVersion2) + { + ExitFunction1(nResult = -1); + } nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor); if (0 != nResult) diff --git a/src/test/DUtilUnitTest/ApupUtilTests.cpp b/src/test/DUtilUnitTest/ApupUtilTests.cpp new file mode 100644 index 00000000..30a45f5a --- /dev/null +++ b/src/test/DUtilUnitTest/ApupUtilTests.cpp @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class ApupUtil + { + public: + [Fact] + void AllocChainFromAtomSortsDescending() + { + HRESULT hr = S_OK; + ATOM_FEED* pFeed = NULL; + APPLICATION_UPDATE_CHAIN* pChain = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + XmlInitialize(); + NativeAssert::Succeeded(hr, "Failed to initialize Xml."); + + pin_ptr feedFilePath = PtrToStringChars(TestData::Get("TestData", "ApupUtilTests", "FeedBv2.0.xml")); + hr = AtomParseFromFile(feedFilePath, &pFeed); + NativeAssert::Succeeded(hr, "Failed to parse feed: {0}", feedFilePath); + + hr = ApupAllocChainFromAtom(pFeed, &pChain); + NativeAssert::Succeeded(hr, "Failed to get chain from feed."); + + Assert::Equal(3ul, pChain->cEntries); + NativeAssert::StringEqual(L"Bundle v2.0", pChain->rgEntries[0].wzTitle); + NativeAssert::StringEqual(L"Bundle v1.0", pChain->rgEntries[1].wzTitle); + NativeAssert::StringEqual(L"Bundle v1.0-preview", pChain->rgEntries[2].wzTitle); + } + finally + { + DutilUninitialize(); + } + } + }; +} diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index 942c39f0..32463262 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -30,6 +30,7 @@ rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib + @@ -59,6 +60,9 @@ + + + diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index fdc6d278..4df7af89 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -15,6 +15,9 @@ + + Source Files + Source Files diff --git a/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml b/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml new file mode 100644 index 00000000..d9f961fe --- /dev/null +++ b/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml @@ -0,0 +1,68 @@ + + + + + + BundleB v2.0 + Bundle Subtitle. + 1116353B-7C6E-4C29-BFA1-D4A972CD421D + 2014-07-14T12:39:00.000Z + http://localhost:9999/wix4/BundleB/feed + + manual build + + Bundle v2.0 + v2.0 + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Updated release.</li> + </ul> + + 2.0.0.0 + 2014-11-10T12:39:00.000Z + + + Bundle v1.0 + v1.0 + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Initial release.</li> + </ul> + + + 1.0.0.0 + 2014-11-09T12:39:00.000Z + + + Bundle v1.0-preview + v1.0-preview + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Initial release.</li> + </ul> + + 1.0.0.0 + 2014-11-09T12:39:00.000Z + + diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h index f665fed1..e9f8770b 100644 --- a/src/test/DUtilUnitTest/precomp.h +++ b/src/test/DUtilUnitTest/precomp.h @@ -11,6 +11,8 @@ #include "error.h" #include +#include +#include #include #include #include @@ -21,8 +23,10 @@ #include #include #include +#include +#include // NOTE: this must come after atomutil.h and rssutil.h since it uses them. #include -#include +#include #pragma managed #include -- cgit v1.2.3-55-g6feb From 0c2b4cf3a439eda3e19d20fadfc65ddc7d0394c0 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 10 Apr 2021 16:05:23 -0700 Subject: Integrate fixes that make dutil a little more robust to failure --- src/dutil/cabutil.cpp | 52 +++++++++++++++---- src/dutil/fileutil.cpp | 128 +++++++++++++++++++++++++++++++++++++++++++++++ src/dutil/inc/fileutil.h | 9 ++++ src/dutil/inc/regutil.h | 13 +++++ src/dutil/regutil.cpp | 59 +++++++++++++++++++++- src/dutil/wiutil.cpp | 18 +++++-- 6 files changed, 265 insertions(+), 14 deletions(-) diff --git a/src/dutil/cabutil.cpp b/src/dutil/cabutil.cpp index 4a6f7b7b..5d77e483 100644 --- a/src/dutil/cabutil.cpp +++ b/src/dutil/cabutil.cpp @@ -102,6 +102,29 @@ LExit: } +static HANDLE OpenFileWithRetry( + __in LPCWSTR wzPath, + __in DWORD dwDesiredAccess, + __in DWORD dwCreationDisposition +) +{ + HANDLE hFile = INVALID_HANDLE_VALUE; + + for (DWORD i = 0; i < 30; ++i) + { + hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hFile) + { + break; + } + + ::Sleep(100); + } + + return hFile; +} + + /******************************************************************** CabInitialize - initializes internal static variables @@ -340,6 +363,7 @@ static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData) static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode) { HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; INT_PTR pFile = -1; LPWSTR sczCabFile = NULL; @@ -353,19 +377,24 @@ static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __i hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); - pFile = reinterpret_cast(::CreateFileW(sczCabFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); - if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) + hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING); + if (INVALID_HANDLE_VALUE == hFile) { CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile); } + pFile = reinterpret_cast(hFile); + if (vdw64EmbeddedOffset) { hr = CabExtractSeek(pFile, 0, 0); CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); } + hFile = INVALID_HANDLE_VALUE; + LExit: + ReleaseFileHandle(hFile); ReleaseStr(sczCabFile); return FAILED(hr) ? -1 : pFile; @@ -460,6 +489,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE Assert(pFDINotify->pv); HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; INT_PTR ipResult = 0; // result to return on success CAB_CALLBACK_STRUCT* pccs = static_cast(pFDINotify->pv); @@ -503,7 +533,6 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); } ::LocalFileTimeToFileTime(&ftLocal, &ft); - WCHAR wzPath[MAX_PATH]; hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); @@ -511,21 +540,24 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE hr = ::StringCchCatW(wzPath, countof(wzPath), wz); CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); - ipResult = reinterpret_cast(::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); - if (INVALID_HANDLE_VALUE == reinterpret_cast(ipResult)) + hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS); + if (INVALID_HANDLE_VALUE == hFile) { CabExitWithLastError(hr, "failed to create file: %ls", wzPath); } - ::SetFileTime(reinterpret_cast(ipResult), &ft, &ft, &ft); // try to set the file time (who cares if it fails) + ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) - if (::SetFilePointer(reinterpret_cast(ipResult), pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) + if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) { - if (::SetEndOfFile(reinterpret_cast(ipResult))) + if (::SetEndOfFile(hFile)) { - ::SetFilePointer(reinterpret_cast(ipResult), 0, NULL, FILE_BEGIN); // reset the file pointer + ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer } } + + ipResult = reinterpret_cast(hFile); + hFile = INVALID_HANDLE_VALUE; } else // resource wasn't requested, skip it { @@ -579,5 +611,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE }; LExit: + ReleaseFileHandle(hFile); + return (S_OK == hr) ? ipResult : -1; } diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp index c76017de..06a44b45 100644 --- a/src/dutil/fileutil.cpp +++ b/src/dutil/fileutil.cpp @@ -1105,6 +1105,134 @@ LExit: } +/******************************************************************* + FileCopyUsingHandlesWithProgress + +*******************************************************************/ +extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + __in_opt LPVOID lpData, + __in_opt LPBOOL pbCancel, + __out_opt DWORD64* pcbCopied +) +{ + HRESULT hr = S_OK; + BOOL fStop = FALSE; + BOOL fCanceled = FALSE; + DWORD64 cbTotalCopied = 0; + BYTE rgbData[64 * 1024]; + DWORD cbRead = 0; + + LARGE_INTEGER liSourceSize = { }; + LARGE_INTEGER liTotalCopied = { }; + LARGE_INTEGER liZero = { }; + DWORD dwResult = 0; + + hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart); + FileExitOnFailure(hr, "Failed to get size of source."); + + if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart) + { + liSourceSize.QuadPart = cbCopy; + } + + dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData); + switch (dwResult) + { + case PROGRESS_CONTINUE: + break; + + case PROGRESS_CANCEL: + fCanceled = TRUE; + fStop = TRUE; + break; + + case PROGRESS_STOP: + fStop = TRUE; + break; + + case PROGRESS_QUIET: + lpProgressRoutine = NULL; + break; + } + + // Set size of the target file. + ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN); + + if (!::SetEndOfFile(hTarget)) + { + FileExitWithLastError(hr, "Failed to set end of target file."); + } + + if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN)) + { + FileExitWithLastError(hr, "Failed to reset target file pointer."); + } + + // Copy with progress. + while (!fStop && (0 == cbCopy || cbTotalCopied < cbCopy)) + { + cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); + if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read from source."); + } + + if (cbRead) + { + hr = FileWriteHandle(hTarget, rgbData, cbRead); + FileExitOnFailure(hr, "Failed to write to target."); + + cbTotalCopied += cbRead; + + if (lpProgressRoutine) + { + liTotalCopied.QuadPart = cbTotalCopied; + dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData); + switch (dwResult) + { + case PROGRESS_CONTINUE: + break; + + case PROGRESS_CANCEL: + fCanceled = TRUE; + fStop = TRUE; + break; + + case PROGRESS_STOP: + fStop = TRUE; + break; + + case PROGRESS_QUIET: + lpProgressRoutine = NULL; + break; + } + } + } + else + { + fStop = TRUE; + } + } + +LExit: + if (pbCancel) + { + *pbCancel = fCanceled; + } + + if (pcbCopied) + { + *pcbCopied = cbTotalCopied; + } + + return hr; +} + + /******************************************************************* FileEnsureCopy diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h index 7caa62b8..48830043 100644 --- a/src/dutil/inc/fileutil.h +++ b/src/dutil/inc/fileutil.h @@ -148,6 +148,15 @@ HRESULT DAPI FileCopyUsingHandles( __in DWORD64 cbCopy, __out_opt DWORD64* pcbCopied ); +HRESULT DAPI FileCopyUsingHandlesWithProgress( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + __in_opt LPVOID lpData, + __in_opt LPBOOL pbCancel, + __out_opt DWORD64* pcbCopied + ); HRESULT DAPI FileEnsureCopy( __in_z LPCWSTR wzSource, __in_z LPCWSTR wzTarget, diff --git a/src/dutil/inc/regutil.h b/src/dutil/inc/regutil.h index 2f09d244..75284940 100644 --- a/src/dutil/inc/regutil.h +++ b/src/dutil/inc/regutil.h @@ -226,6 +226,19 @@ HRESULT DAPI RegQueryKey( __out_opt DWORD* pcSubKeys, __out_opt DWORD* pcValues ); +HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ); +BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ); #ifdef __cplusplus } diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp index e1ef19e8..afd2d089 100644 --- a/src/dutil/regutil.cpp +++ b/src/dutil/regutil.cpp @@ -283,7 +283,7 @@ LExit: /******************************************************************** - RegKeyEnum - enumerates a registry key. + RegKeyEnum - enumerates child registry keys. *********************************************************************/ extern "C" HRESULT DAPI RegKeyEnum( @@ -340,7 +340,7 @@ LExit: /******************************************************************** - RegValueEnum - enumerates a registry value. + RegValueEnum - enumerates registry values. *********************************************************************/ HRESULT DAPI RegValueEnum( @@ -939,6 +939,61 @@ LExit: return hr; } +/******************************************************************** +RegKeyReadNumber - reads a DWORD registry key value as a number from +a specified subkey. + +*********************************************************************/ +extern "C" HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegReadNumber(hkKey, wzName, pdwValue); + RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return hr; +} + +/******************************************************************** +RegValueExists - determines whether a named value exists in a +specified subkey. + +*********************************************************************/ +extern "C" BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + DWORD dwType = 0; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegGetType(hkKey, wzName, &dwType); + RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return SUCCEEDED(hr); +} static HRESULT WriteStringToRegistry( __in HKEY hk, diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp index ffbfe85a..f1984266 100644 --- a/src/dutil/wiutil.cpp +++ b/src/dutil/wiutil.cpp @@ -4,6 +4,7 @@ // Exit macros +#define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) #define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) #define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) #define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) @@ -692,12 +693,23 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( if (fReturnHighestVersionOnly) { - // get the version + // try to get the version but if the product registration is broken + // (for whatever reason), skip this product hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); - WiuExitOnFailure(hr, "Failed to get version for product code: %ls", wzCurrentProductCode); + if (FAILED(hr)) + { + WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode); + continue; + } + // try to parse the product version but if it is corrupt (for whatever + // reason), skip it hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwCurrentVersion); - WiuExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for product code: %ls", sczInstalledVersion, wzCurrentProductCode); + if (FAILED(hr)) + { + WiuExitTrace(hr, "Could not convert version: %ls to DWORD64 for product code: %ls, skipping...", sczInstalledVersion, wzCurrentProductCode); + continue; + } // if this is the first product found then it is the highest version (for now) if (0 == *pcRelatedProducts) -- cgit v1.2.3-55-g6feb From b2c4600453f926fbfdc63219126930ad39601f25 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 8 Apr 2021 14:06:21 -0700 Subject: Port missing WiX v3 changes --- src/dutil/dirutil.cpp | 31 +++++++++ src/dutil/inc/dirutil.h | 5 ++ src/dutil/inc/locutil.h | 12 ++++ src/dutil/inc/pathutil.h | 8 +++ src/dutil/locutil.cpp | 29 +++++--- src/dutil/pathutil.cpp | 33 +++++++++ src/dutil/sqlutil.cpp | 171 ++++++++++++++++++++++++++++++++++++++++------- 7 files changed, 254 insertions(+), 35 deletions(-) diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp index 81130a8d..b10e71f3 100644 --- a/src/dutil/dirutil.cpp +++ b/src/dutil/dirutil.cpp @@ -352,6 +352,37 @@ LExit: } +/******************************************************************* +DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many + of its parents as possible. + + Returns: count of directories deleted. +*******************************************************************/ +extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot( + __in_z LPCWSTR wzPath, + __in DWORD /*dwFlags*/ + ) +{ + DWORD cDeletedDirs = 0; + LPWSTR sczPath = NULL; + + while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) + { + ++cDeletedDirs; + + HRESULT hr = PathGetParentPath(wzPath, &sczPath); + DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); + + wzPath = sczPath; + } + +LExit: + ReleaseStr(sczPath); + + return cDeletedDirs; +} + + /******************************************************************* DirGetCurrent - gets the current directory. diff --git a/src/dutil/inc/dirutil.h b/src/dutil/inc/dirutil.h index 0a19a9c0..539b3a73 100644 --- a/src/dutil/inc/dirutil.h +++ b/src/dutil/inc/dirutil.h @@ -40,6 +40,11 @@ HRESULT DAPI DirEnsureDeleteEx( __in DWORD dwFlags ); +DWORD DAPI DirDeleteEmptyDirectoriesToRoot( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ); + HRESULT DAPI DirGetCurrent( __deref_out_z LPWSTR* psczCurrentDirectory ); diff --git a/src/dutil/inc/locutil.h b/src/dutil/inc/locutil.h index 38ddda20..626cb59e 100644 --- a/src/dutil/inc/locutil.h +++ b/src/dutil/inc/locutil.h @@ -49,6 +49,18 @@ HRESULT DAPI LocProbeForFile( __inout LPWSTR* psczPath ); +/******************************************************************** + LocProbeForFileEx - Searches for a localization file on disk. + useUILanguage should be set to TRUE. +*******************************************************************/ +HRESULT DAPI LocProbeForFileEx( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath, + __in BOOL fUseUILanguage + ); + /******************************************************************** LocLoadFromFile - Loads a localization file diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h index f4f4e59c..719ee7d8 100644 --- a/src/dutil/inc/pathutil.h +++ b/src/dutil/inc/pathutil.h @@ -46,6 +46,14 @@ DAPI_(HRESULT) PathGetDirectory( __out_z LPWSTR *psczDirectory ); +/******************************************************************* +PathGetParentPath - extracts the parent directory from a full path. +********************************************************************/ +DAPI_(HRESULT) PathGetParentPath( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczDirectory + ); + /******************************************************************* PathExpand - gets the full path to a file resolving environment variables along the way. diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp index c4567c03..43e1bb5b 100644 --- a/src/dutil/locutil.cpp +++ b/src/dutil/locutil.cpp @@ -51,7 +51,7 @@ static HRESULT ParseWxlControl( #ifndef MUI_MERGE_SYSTEM_FALLBACK #define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages #endif -typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( +typedef WINBASEAPI BOOL (WINAPI *PFN_GET_THREAD_PREFERRED_UI_LANGUAGES) ( __in DWORD dwFlags, __out PULONG pulNumLanguages, __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, @@ -64,15 +64,26 @@ extern "C" HRESULT DAPI LocProbeForFile( __in_z_opt LPCWSTR wzLanguage, __inout LPWSTR* psczPath ) +{ + return LocProbeForFileEx(wzBasePath, wzLocFileName, wzLanguage, psczPath, FALSE); +} + +extern "C" HRESULT DAPI LocProbeForFileEx( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath, + __in BOOL fUseUILanguage + ) { HRESULT hr = S_OK; LPWSTR sczProbePath = NULL; LANGID langid = 0; LPWSTR sczLangIdFile = NULL; LPWSTR sczLangsBuff = NULL; - GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = - reinterpret_cast( - GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); + PFN_GET_THREAD_PREFERRED_UI_LANGUAGES pfnGetThreadPreferredUILanguages = + reinterpret_cast( + ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); // If a language was specified, look for a loc file in that as a directory. if (wzLanguage && *wzLanguage) @@ -89,12 +100,12 @@ extern "C" HRESULT DAPI LocProbeForFile( } } - if (pvfnGetThreadPreferredUILanguages) + if (fUseUILanguage && pfnGetThreadPreferredUILanguages) { - ULONG nLangs; + ULONG nLangs = 0; ULONG cchLangs = 0; DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; - if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) + if (!(*pfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) { LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); } @@ -103,7 +114,7 @@ extern "C" HRESULT DAPI LocProbeForFile( LocExitOnFailure(hr, "Failed to allocate buffer for languages"); nLangs = 0; - if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) + if (!(*pfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) { LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); } @@ -129,7 +140,7 @@ extern "C" HRESULT DAPI LocProbeForFile( } } - langid = ::GetUserDefaultUILanguage(); + langid = fUseUILanguage ? ::GetUserDefaultUILanguage() : ::GetUserDefaultLangID(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); LocExitOnFailure(hr, "Failed to format user langid."); diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp index ec338f71..183849ac 100644 --- a/src/dutil/pathutil.cpp +++ b/src/dutil/pathutil.cpp @@ -215,6 +215,39 @@ LExit: } +DAPI_(HRESULT) PathGetParentPath( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczParent + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzParent = NULL; + + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + if (wz[1] && (L'\\' == *wz || L'/' == *wz)) + { + wzParent = wz; + } + } + + if (wzParent) + { + DWORD cchPath = static_cast(wzParent - wzPath) + 1; + + hr = StrAllocString(psczParent, wzPath, cchPath); + PathExitOnFailure(hr, "Failed to copy directory."); + } + else + { + ReleaseNullStr(psczParent); + } + +LExit: + return hr; +} + + DAPI_(HRESULT) PathExpand( __out LPWSTR *psczFullPath, __in_z LPCWSTR wzRelativePath, diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp index 63ee80ac..782c7088 100644 --- a/src/dutil/sqlutil.cpp +++ b/src/dutil/sqlutil.cpp @@ -10,7 +10,22 @@ #include "sqlutil.h" +//Please note that only SQL native client 11 has TLS1.2 support +#define _SQLNCLI_OLEDB_DEPRECATE_WARNING + +#if !defined(SQLNCLI_VER) +#define SQLNCLI_VER 1100 +#endif + +#if SQLNCLI_VER >= 1100 +#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) +#define SQLNCLI_CLSID CLSID_SQLNCLI11 +#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) +extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } }; +#endif // SQLNCLI_VER >= 1100 + // Exit macros +#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) #define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) #define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) #define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) @@ -25,11 +40,18 @@ #define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__) // private prototypes +static HRESULT InitializeDatabaseConnection( + __in REFCLSID rclsid, + __in_z LPCSTR szFriendlyClsidName, + __in DBPROPSET rgdbpsetInit[], + __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, + __out IDBCreateSession** ppidbSession + ); +HRESULT DumpErrorRecords(); static HRESULT FileSpecToString( __in const SQL_FILESPEC* psf, __out LPWSTR* ppwz ); - static HRESULT EscapeSqlIdentifier( __in_z LPCWSTR wzDatabase, __deref_out_z LPWSTR* ppwz @@ -55,22 +77,11 @@ extern "C" HRESULT DAPI SqlConnectDatabase( Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession); HRESULT hr = S_OK; - IDBInitialize* pidbInitialize = NULL; - IDBProperties* pidbProperties = NULL; - LPWSTR pwzServerInstance = NULL; - DBPROP rgdbpInit[4]; - DBPROPSET rgdbpsetInit[1]; + DBPROP rgdbpInit[4] = { }; + DBPROPSET rgdbpsetInit[1] = { }; ULONG cProperties = 0; - memset(rgdbpInit, 0, sizeof(rgdbpInit)); - memset(rgdbpsetInit, 0, sizeof(rgdbpsetInit)); - - //obtain access to the SQLOLEDB provider - hr = ::CoCreateInstance(CLSID_SQLOLEDB, NULL, CLSCTX_INPROC_SERVER, - IID_IDBInitialize, (LPVOID*)&pidbInitialize); - SqlExitOnFailure(hr, "failed to create IID_IDBInitialize object"); - // if there is an instance if (wzInstance && *wzInstance) { @@ -137,17 +148,23 @@ extern "C" HRESULT DAPI SqlConnectDatabase( rgdbpsetInit[0].rgProperties = rgdbpInit; rgdbpsetInit[0].cProperties = cProperties; - // create and set the property set - hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); - SqlExitOnFailure(hr, "failed to get IID_IDBProperties object"); - hr = pidbProperties->SetProperties(1, rgdbpsetInit); - SqlExitOnFailure(hr, "failed to set properties"); - - //initialize connection to datasource - hr = pidbInitialize->Initialize(); - SqlExitOnFailure(hr, "failed to initialize connection to database: %ls", wzDatabase); + // obtain access to the SQL Native Client provider + hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); + if (FAILED(hr)) + { + SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB..."); - hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); + // try OLE DB but if that fails return original error failure + HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); + if (FAILED(hr2)) + { + SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up."); + } + else + { + hr = S_OK; + } + } LExit: for (; 0 < cProperties; cProperties--) @@ -155,8 +172,6 @@ LExit: ::VariantClear(&rgdbpInit[cProperties - 1].vValue); } - ReleaseObject(pidbProperties); - ReleaseObject(pidbInitialize); ReleaseStr(pwzServerInstance); return hr; @@ -787,6 +802,110 @@ LExit: // private // +static HRESULT InitializeDatabaseConnection( + __in REFCLSID rclsid, + __in_z LPCSTR szFriendlyClsidName, + __in DBPROPSET rgdbpsetInit[], + __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, + __out IDBCreateSession** ppidbSession +) +{ + Unused(szFriendlyClsidName); // only used in DEBUG builds + + HRESULT hr = S_OK; + IDBInitialize* pidbInitialize = NULL; + IDBProperties* pidbProperties = NULL; + + hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize); + SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName); + + // create and set the property set + hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); + SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName); + + hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit); + SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName); + + // initialize connection to datasource + hr = pidbInitialize->Initialize(); + if (FAILED(hr)) + { + DumpErrorRecords(); + } + SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName); + + hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); + SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName); + +LExit: + ReleaseObject(pidbProperties); + ReleaseObject(pidbInitialize); + + return hr; +} + +HRESULT DumpErrorRecords() +{ + HRESULT hr = S_OK; + IErrorInfo* pIErrorInfo = NULL; + IErrorRecords* pIErrorRecords = NULL; + IErrorInfo* pIErrorInfoRecord = NULL; + BSTR bstrDescription = NULL; + ULONG i = 0; + ULONG cRecords = 0; + ERRORINFO ErrorInfo = { }; + + // Get IErrorInfo pointer from OLE. + hr = ::GetErrorInfo(0, &pIErrorInfo); + if (FAILED(hr)) + { + ExitFunction(); + } + + // QI for IID_IErrorRecords. + hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); + if (FAILED(hr)) + { + ExitFunction(); + } + + // Get error record count. + hr = pIErrorRecords->GetRecordCount(&cRecords); + if (FAILED(hr)) + { + ExitFunction(); + } + + // Loop through the error records. + for (i = 0; i < cRecords; i++) + { + // Get pIErrorInfo from pIErrorRecords. + hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord); + + if (SUCCEEDED(hr)) + { + // Get error description and source. + hr = pIErrorInfoRecord->GetDescription(&bstrDescription); + + // Retrieve the ErrorInfo structures. + hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo); + + SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription); + + ReleaseNullObject(pIErrorInfoRecord); + ReleaseNullBSTR(bstrDescription); + } + } + +LExit: + ReleaseNullBSTR(bstrDescription); + ReleaseObject(pIErrorInfoRecord); + ReleaseObject(pIErrorRecords); + ReleaseObject(pIErrorInfo); + + return hr; +} + /******************************************************************** FileSpecToString -- cgit v1.2.3-55-g6feb From 2113f2bedbdf5c2f8fb21fc5dfacc6ddc7379fe7 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 12 Apr 2021 15:08:52 -0700 Subject: Make FileCopyUsingHandlesWithProgress more like MoveFileWithProgress FileCopyUsingHandlesWithProgress now returns ERROR_REQUEST_ABORTED as an HRESULT when canceled or stopping rather than return success. --- src/dutil/fileutil.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp index 06a44b45..cb67813d 100644 --- a/src/dutil/fileutil.cpp +++ b/src/dutil/fileutil.cpp @@ -1120,7 +1120,6 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( ) { HRESULT hr = S_OK; - BOOL fStop = FALSE; BOOL fCanceled = FALSE; DWORD64 cbTotalCopied = 0; BYTE rgbData[64 * 1024]; @@ -1147,12 +1146,10 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( case PROGRESS_CANCEL: fCanceled = TRUE; - fStop = TRUE; - break; + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); case PROGRESS_STOP: - fStop = TRUE; - break; + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); case PROGRESS_QUIET: lpProgressRoutine = NULL; @@ -1173,7 +1170,7 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( } // Copy with progress. - while (!fStop && (0 == cbCopy || cbTotalCopied < cbCopy)) + while (0 == cbCopy || cbTotalCopied < cbCopy) { cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) @@ -1199,12 +1196,10 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( case PROGRESS_CANCEL: fCanceled = TRUE; - fStop = TRUE; - break; + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); case PROGRESS_STOP: - fStop = TRUE; - break; + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); case PROGRESS_QUIET: lpProgressRoutine = NULL; @@ -1214,7 +1209,7 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( } else { - fStop = TRUE; + break; } } -- cgit v1.2.3-55-g6feb From 10ea1d3e52217ccd93dfa6830776a6be308ca1f6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 12 Apr 2021 20:40:20 -0500 Subject: Use verutil for handling versions in WiuEnumRelatedProductCodes. --- src/dutil/wiutil.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp index f1984266..7414ac42 100644 --- a/src/dutil/wiutil.cpp +++ b/src/dutil/wiutil.cpp @@ -674,8 +674,9 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( HRESULT hr = S_OK; WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { }; LPWSTR sczInstalledVersion = NULL; - DWORD64 qwCurrentVersion = 0; - DWORD64 qwHighestVersion = 0; + VERUTIL_VERSION* pCurrentVersion = NULL; + VERUTIL_VERSION* pHighestVersion = NULL; + int nCompare = 0; // make sure we start at zero *pcRelatedProducts = 0; @@ -702,31 +703,40 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( continue; } - // try to parse the product version but if it is corrupt (for whatever - // reason), skip it - hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwCurrentVersion); - if (FAILED(hr)) + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCurrentVersion); + WiuExitOnFailure(hr, "Failed to parse version: %ls for product code: %ls", sczInstalledVersion, wzCurrentProductCode); + + if (pCurrentVersion->fInvalid) { - WiuExitTrace(hr, "Could not convert version: %ls to DWORD64 for product code: %ls, skipping...", sczInstalledVersion, wzCurrentProductCode); - continue; + WiuExitTrace(E_INVALIDDATA, "Enumerated msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'"); } // if this is the first product found then it is the highest version (for now) - if (0 == *pcRelatedProducts) + if (!pHighestVersion) { - qwHighestVersion = qwCurrentVersion; + pHighestVersion = pCurrentVersion; + pCurrentVersion = NULL; } else { + hr = VerCompareParsedVersions(pCurrentVersion, pHighestVersion, &nCompare); + WiuExitOnFailure(hr, "Failed to compare version '%ls' to highest version: '%ls'", pCurrentVersion->sczVersion, pHighestVersion->sczVersion); + // if this is the highest version encountered so far then overwrite // the first item in the array (there will never be more than one item) - if (qwCurrentVersion > qwHighestVersion) + if (nCompare > 0) { - qwHighestVersion = qwCurrentVersion; + ReleaseVerutilVersion(pHighestVersion); + pHighestVersion = pCurrentVersion; + pCurrentVersion = NULL; hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); WiuExitOnFailure(hr, "Failed to update array with higher versioned product code."); } + else + { + ReleaseVerutilVersion(pCurrentVersion); + } // continue here as we don't want anything else added to the list continue; @@ -738,6 +748,8 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( } LExit: + ReleaseVerutilVersion(pCurrentVersion); + ReleaseVerutilVersion(pHighestVersion); ReleaseStr(sczInstalledVersion); return hr; } -- cgit v1.2.3-55-g6feb From 10a2a1740616715f367536b9515bad5ff6c645e1 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 14 Apr 2021 09:45:51 -0700 Subject: Make FileCopyUsingHandlesWithProgress more like MoveFileWithProgress --- src/dutil/fileutil.cpp | 17 +---------------- src/dutil/inc/fileutil.h | 4 +--- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp index cb67813d..6cc2a80e 100644 --- a/src/dutil/fileutil.cpp +++ b/src/dutil/fileutil.cpp @@ -1114,13 +1114,10 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( __in HANDLE hTarget, __in DWORD64 cbCopy, __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - __in_opt LPVOID lpData, - __in_opt LPBOOL pbCancel, - __out_opt DWORD64* pcbCopied + __in_opt LPVOID lpData ) { HRESULT hr = S_OK; - BOOL fCanceled = FALSE; DWORD64 cbTotalCopied = 0; BYTE rgbData[64 * 1024]; DWORD cbRead = 0; @@ -1145,7 +1142,6 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( break; case PROGRESS_CANCEL: - fCanceled = TRUE; ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); case PROGRESS_STOP: @@ -1195,7 +1191,6 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( break; case PROGRESS_CANCEL: - fCanceled = TRUE; ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); case PROGRESS_STOP: @@ -1214,16 +1209,6 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( } LExit: - if (pbCancel) - { - *pbCancel = fCanceled; - } - - if (pcbCopied) - { - *pcbCopied = cbTotalCopied; - } - return hr; } diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h index 48830043..319c5508 100644 --- a/src/dutil/inc/fileutil.h +++ b/src/dutil/inc/fileutil.h @@ -153,9 +153,7 @@ HRESULT DAPI FileCopyUsingHandlesWithProgress( __in HANDLE hTarget, __in DWORD64 cbCopy, __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - __in_opt LPVOID lpData, - __in_opt LPBOOL pbCancel, - __out_opt DWORD64* pcbCopied + __in_opt LPVOID lpData ); HRESULT DAPI FileEnsureCopy( __in_z LPCWSTR wzSource, -- cgit v1.2.3-55-g6feb From d73c29407fe5ec6a0207af7d9c2547457ae0854c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 20 Apr 2021 00:48:50 -0700 Subject: Unport LocProbeForFileEx() as WiX v4 had better solution --- src/dutil/inc/locutil.h | 12 ------------ src/dutil/locutil.cpp | 29 +++++++++-------------------- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/src/dutil/inc/locutil.h b/src/dutil/inc/locutil.h index 626cb59e..38ddda20 100644 --- a/src/dutil/inc/locutil.h +++ b/src/dutil/inc/locutil.h @@ -49,18 +49,6 @@ HRESULT DAPI LocProbeForFile( __inout LPWSTR* psczPath ); -/******************************************************************** - LocProbeForFileEx - Searches for a localization file on disk. - useUILanguage should be set to TRUE. -*******************************************************************/ -HRESULT DAPI LocProbeForFileEx( - __in_z LPCWSTR wzBasePath, - __in_z LPCWSTR wzLocFileName, - __in_z_opt LPCWSTR wzLanguage, - __inout LPWSTR* psczPath, - __in BOOL fUseUILanguage - ); - /******************************************************************** LocLoadFromFile - Loads a localization file diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp index 43e1bb5b..c4567c03 100644 --- a/src/dutil/locutil.cpp +++ b/src/dutil/locutil.cpp @@ -51,7 +51,7 @@ static HRESULT ParseWxlControl( #ifndef MUI_MERGE_SYSTEM_FALLBACK #define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages #endif -typedef WINBASEAPI BOOL (WINAPI *PFN_GET_THREAD_PREFERRED_UI_LANGUAGES) ( +typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( __in DWORD dwFlags, __out PULONG pulNumLanguages, __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, @@ -64,26 +64,15 @@ extern "C" HRESULT DAPI LocProbeForFile( __in_z_opt LPCWSTR wzLanguage, __inout LPWSTR* psczPath ) -{ - return LocProbeForFileEx(wzBasePath, wzLocFileName, wzLanguage, psczPath, FALSE); -} - -extern "C" HRESULT DAPI LocProbeForFileEx( - __in_z LPCWSTR wzBasePath, - __in_z LPCWSTR wzLocFileName, - __in_z_opt LPCWSTR wzLanguage, - __inout LPWSTR* psczPath, - __in BOOL fUseUILanguage - ) { HRESULT hr = S_OK; LPWSTR sczProbePath = NULL; LANGID langid = 0; LPWSTR sczLangIdFile = NULL; LPWSTR sczLangsBuff = NULL; - PFN_GET_THREAD_PREFERRED_UI_LANGUAGES pfnGetThreadPreferredUILanguages = - reinterpret_cast( - ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); + GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = + reinterpret_cast( + GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); // If a language was specified, look for a loc file in that as a directory. if (wzLanguage && *wzLanguage) @@ -100,12 +89,12 @@ extern "C" HRESULT DAPI LocProbeForFileEx( } } - if (fUseUILanguage && pfnGetThreadPreferredUILanguages) + if (pvfnGetThreadPreferredUILanguages) { - ULONG nLangs = 0; + ULONG nLangs; ULONG cchLangs = 0; DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; - if (!(*pfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) { LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); } @@ -114,7 +103,7 @@ extern "C" HRESULT DAPI LocProbeForFileEx( LocExitOnFailure(hr, "Failed to allocate buffer for languages"); nLangs = 0; - if (!(*pfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) { LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); } @@ -140,7 +129,7 @@ extern "C" HRESULT DAPI LocProbeForFileEx( } } - langid = fUseUILanguage ? ::GetUserDefaultUILanguage() : ::GetUserDefaultLangID(); + langid = ::GetUserDefaultUILanguage(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); LocExitOnFailure(hr, "Failed to format user langid."); -- cgit v1.2.3-55-g6feb From bcd3ee7ab858d62beb36af9f5986544b68a3dd35 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 28 Apr 2021 16:36:56 -0500 Subject: Clean up more 32-bit assumptions. --- src/dutil/acl2util.cpp | 10 +-- src/dutil/apuputil.cpp | 2 +- src/dutil/buffutil.cpp | 126 ++++++++++++++++++--------- src/dutil/cryputil.cpp | 16 +++- src/dutil/deputil.cpp | 12 +-- src/dutil/dirutil.cpp | 2 +- src/dutil/dutil.cpp | 10 ++- src/dutil/eseutil.cpp | 31 +++++-- src/dutil/fileutil.cpp | 149 +++++++++++++++++++++----------- src/dutil/inc/aclutil.h | 2 +- src/dutil/inc/buffutil.h | 2 +- src/dutil/inc/fileutil.h | 7 +- src/dutil/inc/pathutil.h | 12 +++ src/dutil/inc/strutil.h | 4 +- src/dutil/inc/verutil.h | 2 +- src/dutil/inetutil.cpp | 11 ++- src/dutil/logutil.cpp | 6 +- src/dutil/metautil.cpp | 9 +- src/dutil/pathutil.cpp | 58 +++++++++---- src/dutil/regutil.cpp | 18 ++-- src/dutil/reswutil.cpp | 7 +- src/dutil/strutil.cpp | 129 ++++++++++++++++++--------- src/dutil/thmutil.cpp | 5 +- src/dutil/verutil.cpp | 11 ++- src/dutil/xmlutil.cpp | 2 +- src/test/DUtilUnitTest/FileUtilTest.cpp | 10 ++- 26 files changed, 446 insertions(+), 207 deletions(-) diff --git a/src/dutil/acl2util.cpp b/src/dutil/acl2util.cpp index 5aba60f0..598f12e7 100644 --- a/src/dutil/acl2util.cpp +++ b/src/dutil/acl2util.cpp @@ -23,7 +23,7 @@ NOTE: psczSid should be freed with StrFree() ********************************************************************/ extern "C" HRESULT DAPI AclCalculateServiceSidString( __in LPCWSTR wzServiceName, - __in int cchServiceName, + __in SIZE_T cchServiceName, __deref_out_z LPWSTR* psczSid ) { @@ -39,7 +39,7 @@ extern "C" HRESULT DAPI AclCalculateServiceSidString( if (0 == cchServiceName) { - hr = ::StringCchLengthW(wzServiceName, INT_MAX, reinterpret_cast(&cchServiceName)); + hr = ::StringCchLengthW(wzServiceName, STRSAFE_MAX_CCH, reinterpret_cast(&cchServiceName)); AclExitOnFailure(hr, "Failed to get the length of the service name."); } @@ -49,7 +49,7 @@ extern "C" HRESULT DAPI AclCalculateServiceSidString( pbHash = reinterpret_cast(MemAlloc(cbHash, TRUE)); AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); - hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * 2, PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); + hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * sizeof(WCHAR), PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u", @@ -80,7 +80,7 @@ extern "C" HRESULT DAPI AclGetAccountSidStringEx( ) { HRESULT hr = S_OK; - int cchAccount = 0; + SIZE_T cchAccount = 0; PSID psid = NULL; LPWSTR pwz = NULL; LPWSTR sczSid = NULL; @@ -103,7 +103,7 @@ extern "C" HRESULT DAPI AclGetAccountSidStringEx( { if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) { - HRESULT hrLength = ::StringCchLengthW(wzAccount, INT_MAX, reinterpret_cast(&cchAccount)); + HRESULT hrLength = ::StringCchLengthW(wzAccount, STRSAFE_MAX_CCH, reinterpret_cast(&cchAccount)); AclExitOnFailure(hrLength, "Failed to get the length of the account name."); if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11)) diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp index 6f5825bb..eb96d515 100644 --- a/src/dutil/apuputil.cpp +++ b/src/dutil/apuputil.cpp @@ -388,7 +388,7 @@ static HRESULT ParseEnclosure( if (dwDigestStringLength != cchDigestString) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Invalid digest length (%zu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); + ApupExitOnRootFailure(hr, "Invalid digest length (%Iu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); } pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength; diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp index a6d3ac90..b6d58cc0 100644 --- a/src/dutil/buffutil.cpp +++ b/src/dutil/buffutil.cpp @@ -70,7 +70,7 @@ extern "C" HRESULT BuffReadNumber64( __in SIZE_T cbBuffer, __inout SIZE_T* piBuffer, __out DWORD64* pdw64 -) + ) { Assert(pbBuffer); Assert(piBuffer); @@ -98,11 +98,11 @@ LExit: } extern "C" HRESULT BuffReadPointer( - __in_bcount(cbBuffer) const BYTE * pbBuffer, + __in_bcount(cbBuffer) const BYTE* pbBuffer, __in SIZE_T cbBuffer, __inout SIZE_T* piBuffer, __out DWORD_PTR* pdw64 -) + ) { Assert(pbBuffer); Assert(piBuffer); @@ -141,8 +141,8 @@ extern "C" HRESULT BuffReadString( Assert(pscz); HRESULT hr = S_OK; - DWORD cch = 0; - DWORD cb = 0; + SIZE_T cch = 0; + SIZE_T cb = 0; SIZE_T cbAvailable = 0; // get availiable data size @@ -150,19 +150,19 @@ extern "C" HRESULT BuffReadString( BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); // verify buffer size - if (sizeof(DWORD) > cbAvailable) + if (sizeof(SIZE_T) > cbAvailable) { hr = E_INVALIDARG; BuffExitOnRootFailure(hr, "Buffer too small."); } // read character count - cch = *(const DWORD*)(pbBuffer + *piBuffer); + cch = *(const SIZE_T*)(pbBuffer + *piBuffer); - hr = ::DWordMult(cch, static_cast(sizeof(WCHAR)), &cb); + hr = ::SIZETMult(cch, sizeof(WCHAR), &cb); BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); - hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer); + hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); // get availiable data size @@ -198,8 +198,8 @@ extern "C" HRESULT BuffReadStringAnsi( Assert(pscz); HRESULT hr = S_OK; - DWORD cch = 0; - DWORD cb = 0; + SIZE_T cch = 0; + SIZE_T cb = 0; SIZE_T cbAvailable = 0; // get availiable data size @@ -207,19 +207,19 @@ extern "C" HRESULT BuffReadStringAnsi( BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); // verify buffer size - if (sizeof(DWORD) > cbAvailable) + if (sizeof(SIZE_T) > cbAvailable) { hr = E_INVALIDARG; BuffExitOnRootFailure(hr, "Buffer too small."); } // read character count - cch = *(const DWORD*)(pbBuffer + *piBuffer); + cch = *(const SIZE_T*)(pbBuffer + *piBuffer); - hr = ::DWordMult(cch, static_cast(sizeof(CHAR)), &cb); + hr = ::SIZETMult(cch, sizeof(CHAR), &cb); BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); - hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer); + hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); // get availiable data size @@ -257,23 +257,24 @@ extern "C" HRESULT BuffReadStream( Assert(pcbStream); HRESULT hr = S_OK; - DWORD64 cb = 0; + SIZE_T cb = 0; SIZE_T cbAvailable = 0; + errno_t err = 0; // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); // verify buffer size - if (sizeof(DWORD64) > cbAvailable) + if (sizeof(SIZE_T) > cbAvailable) { hr = E_INVALIDARG; BuffExitOnRootFailure(hr, "Buffer too small."); } // read stream size - cb = *(const DWORD64*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(DWORD64); + cb = *(const SIZE_T*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(SIZE_T); // get availiable data size hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); @@ -287,15 +288,20 @@ extern "C" HRESULT BuffReadStream( } // allocate buffer - *ppbStream = (BYTE*)MemAlloc((SIZE_T)cb, TRUE); + *ppbStream = (BYTE*)MemAlloc(cb, TRUE); BuffExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); // read stream data - memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, (SIZE_T)cb); - *piBuffer += (SIZE_T)cb; + err = memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to read stream from buffer, error: %d", err); + } + + *piBuffer += cb; // return stream size - *pcbStream = (SIZE_T)cb; + *pcbStream = cb; LExit: return hr; @@ -304,7 +310,7 @@ LExit: extern "C" HRESULT BuffWriteNumber( __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, - __in DWORD_PTR dw + __in DWORD dw ) { Assert(ppbBuffer); @@ -317,7 +323,7 @@ extern "C" HRESULT BuffWriteNumber( BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy data to buffer - *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; + *(DWORD*)(*ppbBuffer + *piBuffer) = dw; *piBuffer += sizeof(DWORD); LExit: @@ -351,7 +357,7 @@ extern "C" HRESULT BuffWritePointer( __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, __in DWORD_PTR dw -) + ) { Assert(ppbBuffer); Assert(piBuffer); @@ -380,19 +386,33 @@ extern "C" HRESULT BuffWriteString( Assert(piBuffer); HRESULT hr = S_OK; - DWORD cch = (DWORD)lstrlenW(scz); - SIZE_T cb = cch * sizeof(WCHAR); + SIZE_T cch = 0; + SIZE_T cb = 0; + errno_t err = 0; + + if (scz) + { + hr = ::StringCchLengthW(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); + BuffExitOnRootFailure(hr, "Failed to get string size.") + } + + cb = cch * sizeof(WCHAR); // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb)); + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy character count to buffer - *(DWORD*)(*ppbBuffer + *piBuffer) = cch; - *piBuffer += sizeof(DWORD); + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(SIZE_T); // copy data to buffer - memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%ls', error: %d", scz, err); + } + *piBuffer += cb; LExit: @@ -409,19 +429,33 @@ extern "C" HRESULT BuffWriteStringAnsi( Assert(piBuffer); HRESULT hr = S_OK; - DWORD cch = (DWORD)lstrlenA(scz); - SIZE_T cb = cch * sizeof(CHAR); + SIZE_T cch = 0; + SIZE_T cb = 0; + errno_t err = 0; + + if (scz) + { + hr = ::StringCchLengthA(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); + BuffExitOnRootFailure(hr, "Failed to get string size.") + } + + cb = cch * sizeof(CHAR); // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb)); + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy character count to buffer - *(DWORD*)(*ppbBuffer + *piBuffer) = cch; - *piBuffer += sizeof(DWORD); + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(SIZE_T); // copy data to buffer - memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%hs', error: %d", scz, err); + } + *piBuffer += cb; LExit: @@ -440,18 +474,24 @@ extern "C" HRESULT BuffWriteStream( Assert(pbStream); HRESULT hr = S_OK; - DWORD64 cb = cbStream; + SIZE_T cb = cbStream; + errno_t err = 0; // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(DWORD64)); + hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(SIZE_T)); BuffExitOnFailure(hr, "Failed to ensure buffer size."); // copy byte count to buffer - *(DWORD64*)(*ppbBuffer + *piBuffer) = cb; - *piBuffer += sizeof(DWORD64); + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cb; + *piBuffer += sizeof(SIZE_T); // copy data to buffer - memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream); + err = memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write stream to buffer, error: %d", err); + } + *piBuffer += cbStream; LExit: diff --git a/src/dutil/cryputil.cpp b/src/dutil/cryputil.cpp index c5c1b221..24bb83f1 100644 --- a/src/dutil/cryputil.cpp +++ b/src/dutil/cryputil.cpp @@ -291,6 +291,9 @@ HRESULT DAPI CrypHashBuffer( HRESULT hr = S_OK; HCRYPTPROV hProv = NULL; HCRYPTHASH hHash = NULL; + DWORD cbDataHashed = 0; + SIZE_T cbTotal = 0; + SIZE_T cbRemaining = 0; // get handle to the crypto provider if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) @@ -304,10 +307,17 @@ HRESULT DAPI CrypHashBuffer( CrypExitWithLastError(hr, "Failed to initiate hash."); } - if (!::CryptHashData(hHash, pbBuffer, static_cast(cbBuffer), 0)) + do { - CrypExitWithLastError(hr, "Failed to hash data."); - } + cbRemaining = cbBuffer - cbTotal; + cbDataHashed = (DWORD)min(DWORD_MAX, cbRemaining); + if (!::CryptHashData(hHash, pbBuffer + cbTotal, cbDataHashed, 0)) + { + CrypExitWithLastError(hr, "Failed to hash data."); + } + + cbTotal += cbDataHashed; + } while (cbTotal < cbBuffer); // get hash value if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) diff --git a/src/dutil/deputil.cpp b/src/dutil/deputil.cpp index 362b3345..2e6d6a6c 100644 --- a/src/dutil/deputil.cpp +++ b/src/dutil/deputil.cpp @@ -122,9 +122,7 @@ DAPI_(HRESULT) DepCheckDependency( LPWSTR sczKey = NULL; HKEY hkKey = NULL; DWORD64 dw64Version = 0; - int cchMinVersion = 0; DWORD64 dw64MinVersion = 0; - int cchMaxVersion = 0; DWORD64 dw64MaxVersion = 0; BOOL fAllowEqual = FALSE; LPWSTR sczName = NULL; @@ -171,10 +169,9 @@ DAPI_(HRESULT) DepCheckDependency( // Check MinVersion if provided. if (wzMinVersion) { - cchMinVersion = lstrlenW(wzMinVersion); - if (0 < cchMinVersion) + if (*wzMinVersion) { - hr = FileVersionFromStringEx(wzMinVersion, cchMinVersion, &dw64MinVersion); + hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion); DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; @@ -206,10 +203,9 @@ DAPI_(HRESULT) DepCheckDependency( // Check MaxVersion if provided. if (wzMaxVersion) { - cchMaxVersion = lstrlenW(wzMaxVersion); - if (0 < cchMaxVersion) + if (*wzMaxVersion) { - hr = FileVersionFromStringEx(wzMaxVersion, cchMaxVersion, &dw64MaxVersion); + hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion); DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp index b10e71f3..ae2c5e1c 100644 --- a/src/dutil/dirutil.cpp +++ b/src/dutil/dirutil.cpp @@ -400,7 +400,7 @@ extern "C" HRESULT DAPI DirGetCurrent( DirExitOnFailure(hr, "Failed to determine size of current directory."); } - DWORD cchRequired = ::GetCurrentDirectoryW(static_cast(cch), 0 == cch ? NULL : *psczCurrentDirectory); + DWORD cchRequired = ::GetCurrentDirectoryW((DWORD)min(DWORD_MAX, cch), 0 == cch ? NULL : *psczCurrentDirectory); if (0 == cchRequired) { DirExitWithLastError(hr, "Failed to get current directory."); diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp index c500191a..56b85207 100644 --- a/src/dutil/dutil.cpp +++ b/src/dutil/dutil.cpp @@ -127,9 +127,13 @@ extern "C" void DAPI Dutil_AssertMsg( hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != hAssertFile) { - ::SetFilePointer(hAssertFile, 0, 0, FILE_END); - ::StringCchCatA(szMsg, countof(szMsg), "\r\n"); - ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); + if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END)) + { + if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n"))) + { + ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); + } + } } } } diff --git a/src/dutil/eseutil.cpp b/src/dutil/eseutil.cpp index d2bd7dc5..b9455d4b 100644 --- a/src/dutil/eseutil.cpp +++ b/src/dutil/eseutil.cpp @@ -312,12 +312,14 @@ HRESULT AllocIndexCreateStruct( EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); // Record the size including both null terminators - the struct requires this - DWORD dwSize = 0; - dwSize = lstrlen(pszMultiSzKeys) + 1; // add 1 to include null character at the end - EseExitOnFailure(hr, "Failed to get size of keys string"); + size_t cchSize = 0; + hr = ::StringCchLengthA(pszMultiSzKeys, STRSAFE_MAX_LENGTH, &cchSize); + EseExitOnRootFailure(hr, "Failed to get size of keys string"); + + ++cchSize; // add 1 to include null character at the end // At this point convert all question marks to null characters - for (i = 0; i < dwSize; ++i) + for (i = 0; i < cchSize; ++i) { if ('?' == pszMultiSzKeys[i]) { @@ -328,7 +330,7 @@ HRESULT AllocIndexCreateStruct( (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE); (*ppjicIndexCreate)->szIndexName = pszIndexName; (*ppjicIndexCreate)->szKey = pszMultiSzKeys; - (*ppjicIndexCreate)->cbKey = dwSize; + (*ppjicIndexCreate)->cbKey = (DWORD)cchSize; (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary; (*ppjicIndexCreate)->ulDensity = 80; (*ppjicIndexCreate)->lcid = 1033; @@ -884,7 +886,16 @@ HRESULT DAPI EseSetColumnString( { HRESULT hr = S_OK; JET_ERR jEr = JET_errSuccess; - ULONG cbValueSize = static_cast((wcslen(pwzValue) + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes + size_t cchValue = 0; + ULONG cbValueSize = 0; + + if (pwzValue) + { + hr = ::StringCchLengthW(pwzValue, STRSAFE_MAX_LENGTH, &cchValue); + EseExitOnRootFailure(hr, "Failed to get string length: %ls", pwzValue); + } + + cbValueSize = static_cast((cchValue + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL); ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue); @@ -1196,11 +1207,17 @@ HRESULT DAPI EseSetQueryColumnString( { HRESULT hr = S_OK; DWORD dwStringSize = 0; + size_t cchString = 0; ESE_QUERY *peqHandle = static_cast(eqhHandle); JET_GRBIT jGrb = 0; - dwStringSize = sizeof(WCHAR) * (lstrlenW(pszString) + 1); // Add 1 for null terminator + if (pszString) + { + hr = ::StringCchLengthW(pszString, STRSAFE_MAX_LENGTH, &cchString); + EseExitOnRootFailure(hr, "Failed to get size of column string"); + } + dwStringSize = static_cast(sizeof(WCHAR) * (cchString + 1)); // Add 1 for null terminator if (fFinal) { diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp index 6cc2a80e..1822727a 100644 --- a/src/dutil/fileutil.cpp +++ b/src/dutil/fileutil.cpp @@ -155,40 +155,33 @@ __out LPWSTR *ppwzFileNameNoExtension Assert(wzFileName && *wzFileName); HRESULT hr = S_OK; - - SIZE_T cchFileName = wcslen(wzFileName); - + size_t cchFileName = 0; LPWSTR pwzFileNameNoExtension = NULL; - DWORD cchFileNameNoExtension = 0; - - // Filename without extension can not be longer than _MAX_FNAME - // Filename without extension should also not be longer than filename itself - if (_MAX_FNAME > cchFileName) - { - cchFileNameNoExtension = (DWORD) cchFileName; - } - else - { - cchFileNameNoExtension = _MAX_FNAME; - } - + size_t cchFileNameNoExtension = 0; + errno_t err = 0; + + hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_LENGTH, &cchFileName); + FileExitOnRootFailure(hr, "failed to get length of file name: %ls", wzFileName); + + cchFileNameNoExtension = cchFileName + 1; + hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); FileExitOnFailure(hr, "failed to allocate space for File Name without extension"); - + // _wsplitpath_s can handle drive/path/filename/extension - errno_t err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); - if (0 != err) + err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); + if (err) { hr = E_INVALIDARG; - FileExitOnFailure(hr, "failed to parse filename: %ls", wzFileName); + FileExitOnRootFailure(hr, "failed to parse filename: '%ls', error: %d", wzFileName, err); } - + *ppwzFileNameNoExtension = pwzFileNameNoExtension; pwzFileNameNoExtension = NULL; - + LExit: ReleaseStr(pwzFileNameNoExtension); - + return hr; } @@ -237,8 +230,12 @@ extern "C" HRESULT DAPI FileAddSuffixToBaseName( HRESULT hr = S_OK; LPWSTR sczNewFileName = NULL; + size_t cchFileName = 0; + + hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_CCH, &cchFileName); + FileExitOnRootFailure(hr, "Failed to get length of file name: %ls", wzFileName); - LPCWSTR wzExtension = wzFileName + lstrlenW(wzFileName); + LPCWSTR wzExtension = wzFileName + cchFileName; while (wzFileName < wzExtension && L'.' != *wzExtension) { --wzExtension; @@ -410,7 +407,7 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI FileVersionFromStringEx( __in_z LPCWSTR wzVersion, - __in DWORD cchVersion, + __in SIZE_T cchVersion, __out DWORD64* pqwVersion ) { @@ -428,7 +425,9 @@ extern "C" HRESULT DAPI FileVersionFromStringEx( // get string length if not provided if (0 >= cchVersion) { - cchVersion = lstrlenW(wzVersion); + hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); + FileExitOnRootFailure(hr, "Failed to get length of file version string: %ls", wzVersion); + if (0 >= cchVersion) { ExitFunction1(hr = E_INVALIDARG); @@ -996,6 +995,41 @@ LExit: return hr; } +extern "C" HRESULT DAPI FileReadHandle( + __in HANDLE hFile, + __in_bcount(cbDest) LPBYTE pbDest, + __in SIZE_T cbDest + ) +{ + HRESULT hr = 0; + DWORD cbDataRead = 0; + SIZE_T cbRemaining = cbDest; + SIZE_T cbTotal = 0; + + while (0 < cbRemaining) + { + if (!::ReadFile(hFile, pbDest + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataRead, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_MORE_DATA == er) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + FileExitOnRootFailure(hr, "Failed to read data from file handle."); + } + + cbRemaining -= cbDataRead; + cbTotal += cbDataRead; + } + +LExit: + return hr; +} + /******************************************************************* FileWrite - write a file from memory @@ -1044,18 +1078,20 @@ extern "C" HRESULT DAPI FileWriteHandle( { HRESULT hr = S_OK; DWORD cbDataWritten = 0; - DWORD cbTotal = 0; + SIZE_T cbTotal = 0; + SIZE_T cbRemaining = cbData; // Write out all of the data. - do + while (0 < cbRemaining) { - if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)(cbData - cbTotal), &cbDataWritten, NULL)) + if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataWritten, NULL)) { FileExitOnLastError(hr, "Failed to write data to file handle."); } + cbRemaining -= cbDataWritten; cbTotal += cbDataWritten; - } while (cbTotal < cbData); + } LExit: return hr; @@ -1115,7 +1151,7 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( __in DWORD64 cbCopy, __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, __in_opt LPVOID lpData -) + ) { HRESULT hr = S_OK; DWORD64 cbTotalCopied = 0; @@ -1135,21 +1171,24 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( liSourceSize.QuadPart = cbCopy; } - dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData); - switch (dwResult) + if (lpProgressRoutine) { - case PROGRESS_CONTINUE: - break; + dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData); + switch (dwResult) + { + case PROGRESS_CONTINUE: + break; - case PROGRESS_CANCEL: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + case PROGRESS_CANCEL: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - case PROGRESS_STOP: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + case PROGRESS_STOP: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - case PROGRESS_QUIET: - lpProgressRoutine = NULL; - break; + case PROGRESS_QUIET: + lpProgressRoutine = NULL; + break; + } } // Set size of the target file. @@ -1929,23 +1968,27 @@ extern "C" HRESULT DAPI FileFromString( LPSTR sczUtf8String = NULL; BYTE *pbFullFileBuffer = NULL; const BYTE *pcbFullFileBuffer = NULL; - DWORD cbFullFileBuffer = 0; - DWORD cbStrLen = 0; + SIZE_T cbFullFileBuffer = 0; + SIZE_T cbStrLen = 0; switch (feEncoding) { case FILE_ENCODING_UTF8: hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); - - cbFullFileBuffer = lstrlenA(sczUtf8String); + + hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbFullFileBuffer)); + FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); + pcbFullFileBuffer = reinterpret_cast(sczUtf8String); break; case FILE_ENCODING_UTF8_WITH_BOM: hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); - - cbStrLen = lstrlenA(sczUtf8String); + + hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); + cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); @@ -1956,11 +1999,17 @@ extern "C" HRESULT DAPI FileFromString( pcbFullFileBuffer = pbFullFileBuffer; break; case FILE_ENCODING_UTF16: - cbFullFileBuffer = lstrlenW(sczString) * sizeof(WCHAR); + hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of string"); + + cbFullFileBuffer = cbStrLen * sizeof(WCHAR); pcbFullFileBuffer = reinterpret_cast(sczString); break; case FILE_ENCODING_UTF16_WITH_BOM: - cbStrLen = lstrlenW(sczString) * sizeof(WCHAR); + hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of string"); + + cbStrLen *= sizeof(WCHAR); cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); diff --git a/src/dutil/inc/aclutil.h b/src/dutil/inc/aclutil.h index 144e4613..ac03f9a8 100644 --- a/src/dutil/inc/aclutil.h +++ b/src/dutil/inc/aclutil.h @@ -140,7 +140,7 @@ HRESULT DAPI AclAddAdminToSecurityDescriptor( // Following code in acl2util.cpp due to dependency on crypt32.dll. HRESULT DAPI AclCalculateServiceSidString( __in LPCWSTR wzServiceName, - __in int cchServiceName, + __in SIZE_T cchServiceName, __deref_out_z LPWSTR* psczSid ); HRESULT DAPI AclGetAccountSidStringEx( diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h index 7509f76a..322209e6 100644 --- a/src/dutil/inc/buffutil.h +++ b/src/dutil/inc/buffutil.h @@ -57,7 +57,7 @@ HRESULT BuffReadStream( HRESULT BuffWriteNumber( __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, __inout SIZE_T* piBuffer, - __in DWORD_PTR dw + __in DWORD dw ); HRESULT BuffWriteNumber64( __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h index 319c5508..d3e326f7 100644 --- a/src/dutil/inc/fileutil.h +++ b/src/dutil/inc/fileutil.h @@ -62,7 +62,7 @@ HRESULT DAPI FileVersionFromString( ); HRESULT DAPI FileVersionFromStringEx( __in_z LPCWSTR wzVersion, - __in DWORD cchVersion, + __in SIZE_T cchVersion, __out DWORD64* pqwVersion ); HRESULT DAPI FileVersionToStringEx( @@ -130,6 +130,11 @@ HRESULT DAPI FileReadPartialEx( __in BOOL fPartialOK, __in DWORD dwShareMode ); +HRESULT DAPI FileReadHandle( + __in HANDLE hFile, + __in_bcount(cbDest) LPBYTE pbDest, + __in SIZE_T cbDest + ); HRESULT DAPI FileWrite( __in_z LPCWSTR pwzFileName, __in DWORD dwFlagsAndAttributes, diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h index 719ee7d8..579b8454 100644 --- a/src/dutil/inc/pathutil.h +++ b/src/dutil/inc/pathutil.h @@ -177,6 +177,18 @@ DAPI_(HRESULT) PathConcat( __deref_out_z LPWSTR* psczCombined ); +/******************************************************************* + PathConcatCch - like .NET's Path.Combine, lets you build up a path + one piece -- file or directory -- at a time. +*******************************************************************/ +DAPI_(HRESULT) PathConcatCch( + __in_opt LPCWSTR wzPath1, + __in SIZE_T cchPath1, + __in_opt LPCWSTR wzPath2, + __in SIZE_T cchPath2, + __deref_out_z LPWSTR* psczCombined + ); + /******************************************************************* PathEnsureQuoted - ensures that a path is quoted; optionally, this function also terminates a directory with a backslash diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h index cf8c751c..1cff9ab8 100644 --- a/src/dutil/inc/strutil.h +++ b/src/dutil/inc/strutil.h @@ -277,12 +277,12 @@ void DAPI StrStringToLower( HRESULT DAPI StrAllocStringToUpperInvariant( __deref_out_z LPWSTR* pscz, __in_z LPCWSTR wzSource, - __in int cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrAllocStringToLowerInvariant( __deref_out_z LPWSTR* pscz, __in_z LPCWSTR wzSource, - __in int cchSource + __in SIZE_T cchSource ); HRESULT DAPI StrArrayAllocString( diff --git a/src/dutil/inc/verutil.h b/src/dutil/inc/verutil.h index 3caa17e1..5247bb61 100644 --- a/src/dutil/inc/verutil.h +++ b/src/dutil/inc/verutil.h @@ -74,7 +74,7 @@ void DAPI VerFreeVersion( *******************************************************************/ HRESULT DAPI VerParseVersion( __in_z LPCWSTR wzVersion, - __in DWORD cchVersion, + __in SIZE_T cchVersion, __in BOOL fStrict, __out VERUTIL_VERSION** ppVersion ); diff --git a/src/dutil/inetutil.cpp b/src/dutil/inetutil.cpp index f75849f6..8dace55f 100644 --- a/src/dutil/inetutil.cpp +++ b/src/dutil/inetutil.cpp @@ -86,7 +86,8 @@ extern "C" HRESULT DAPI InternetQueryInfoString( ) { HRESULT hr = S_OK; - DWORD_PTR cbValue = 0; + SIZE_T cbOriginal = 0; + DWORD cbValue = 0; DWORD dwIndex = 0; // If nothing was provided start off with some arbitrary size. @@ -96,10 +97,12 @@ extern "C" HRESULT DAPI InternetQueryInfoString( InetExitOnFailure(hr, "Failed to allocate memory for value."); } - hr = StrSize(*psczValue, &cbValue); + hr = StrSize(*psczValue, &cbOriginal); InetExitOnFailure(hr, "Failed to get size of value."); - if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), reinterpret_cast(&cbValue), &dwIndex)) + cbValue = (DWORD)min(DWORD_MAX, cbOriginal); + + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) { DWORD er = ::GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == er) @@ -109,7 +112,7 @@ extern "C" HRESULT DAPI InternetQueryInfoString( hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR)); InetExitOnFailure(hr, "Failed to allocate value."); - if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), reinterpret_cast(&cbValue), &dwIndex)) + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) { er = ::GetLastError(); } diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp index 35251274..ac68036a 100644 --- a/src/dutil/logutil.cpp +++ b/src/dutil/logutil.cpp @@ -776,11 +776,15 @@ extern "C" HRESULT LogStringWorkRaw( Assert(szLogData && *szLogData); HRESULT hr = S_OK; + size_t cchLogData = 0; DWORD cbLogData = 0; DWORD cbTotal = 0; DWORD cbWrote = 0; - cbLogData = lstrlenA(szLogData); + hr = ::StringCchLengthA(szLogData, STRSAFE_MAX_CCH, &cchLogData); + LoguExitOnRootFailure(hr, "Failed to get length of raw string"); + + cbLogData = (DWORD)cchLogData; // If the log hasn't been initialized yet, store it in a buffer if (INVALID_HANDLE_VALUE == LogUtil_hLog) diff --git a/src/dutil/metautil.cpp b/src/dutil/metautil.cpp index 109cd31e..f313fc1c 100644 --- a/src/dutil/metautil.cpp +++ b/src/dutil/metautil.cpp @@ -299,7 +299,14 @@ extern "C" HRESULT DAPI MetaGetValue( MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); } else // set the size of the data to the actual size of the memory - pmr->dwMDDataLen = (DWORD)MemSize(pmr->pbMDData); + { + SIZE_T cb = MemSize(pmr->pbMDData); + if (cb > DWORD_MAX) + { + MetaExitOnRootFailure(hr = E_INVALIDSTATE, "metabase data is too large: %Iu", cb); + } + pmr->dwMDDataLen = (DWORD)cb; + } hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp index 183849ac..7c3cfe06 100644 --- a/src/dutil/pathutil.cpp +++ b/src/dutil/pathutil.cpp @@ -181,7 +181,7 @@ DAPI_(HRESULT) PathGetDirectory( ) { HRESULT hr = S_OK; - DWORD cchDirectory = DWORD_MAX; + size_t cchDirectory = SIZE_T_MAX; for (LPCWSTR wz = wzPath; *wz; ++wz) { @@ -191,11 +191,11 @@ DAPI_(HRESULT) PathGetDirectory( // : => relative path from mapped root if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) { - cchDirectory = static_cast(wz - wzPath) + 1; + cchDirectory = static_cast(wz - wzPath) + 1; } } - if (DWORD_MAX == cchDirectory) + if (SIZE_T_MAX == cchDirectory) { // we were given just a file name, so there's no directory available return S_FALSE; @@ -233,7 +233,7 @@ DAPI_(HRESULT) PathGetParentPath( if (wzParent) { - DWORD cchPath = static_cast(wzParent - wzPath) + 1; + size_t cchPath = static_cast(wzParent - wzPath) + 1; hr = StrAllocString(psczParent, wzPath, cchPath); PathExitOnFailure(hr, "Failed to copy directory."); @@ -260,6 +260,7 @@ DAPI_(HRESULT) PathExpand( DWORD cch = 0; LPWSTR sczExpandedPath = NULL; DWORD cchExpandedPath = 0; + SIZE_T cbSize = 0; LPWSTR sczFullPath = NULL; @@ -305,8 +306,10 @@ DAPI_(HRESULT) PathExpand( } PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); - hr = StrMaxLength(sczExpandedPath, reinterpret_cast(&cchExpandedPath)); + hr = StrMaxLength(sczExpandedPath, &cbSize); PathExitOnFailure(hr, "Failed to get max length of expanded path."); + + cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize); } } @@ -317,7 +320,7 @@ DAPI_(HRESULT) PathExpand( { LPWSTR wzFileName = NULL; LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; - DWORD cchFullPath = PATH_GOOD_ENOUGH < cchExpandedPath ? cchExpandedPath : PATH_GOOD_ENOUGH; + DWORD cchFullPath = max(PATH_GOOD_ENOUGH, cchExpandedPath); hr = StrAlloc(&sczFullPath, cchFullPath); PathExitOnFailure(hr, "Failed to allocate space for full path."); @@ -836,8 +839,7 @@ DAPI_(BOOL) PathIsAbsolute( __in_z LPCWSTR wzPath ) { - DWORD dwLength = lstrlenW(wzPath); - return (1 < dwLength) && (wzPath[0] == L'\\') || (wzPath[1] == L':'); + return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); } @@ -846,28 +848,40 @@ DAPI_(HRESULT) PathConcat( __in_opt LPCWSTR wzPath2, __deref_out_z LPWSTR* psczCombined ) +{ + return PathConcatCch(wzPath1, 0, wzPath2, 0, psczCombined); +} + + +DAPI_(HRESULT) PathConcatCch( + __in_opt LPCWSTR wzPath1, + __in SIZE_T cchPath1, + __in_opt LPCWSTR wzPath2, + __in SIZE_T cchPath2, + __deref_out_z LPWSTR* psczCombined + ) { HRESULT hr = S_OK; if (!wzPath2 || !*wzPath2) { - hr = StrAllocString(psczCombined, wzPath1, 0); + hr = StrAllocString(psczCombined, wzPath1, cchPath1); PathExitOnFailure(hr, "Failed to copy just path1 to output."); } else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) { - hr = StrAllocString(psczCombined, wzPath2, 0); + hr = StrAllocString(psczCombined, wzPath2, cchPath2); PathExitOnFailure(hr, "Failed to copy just path2 to output."); } else { - hr = StrAllocString(psczCombined, wzPath1, 0); + hr = StrAllocString(psczCombined, wzPath1, cchPath1); PathExitOnFailure(hr, "Failed to copy path1 to output."); hr = PathBackslashTerminate(psczCombined); PathExitOnFailure(hr, "Failed to backslashify."); - hr = StrAllocConcat(psczCombined, wzPath2, 0); + hr = StrAllocConcat(psczCombined, wzPath2, cchPath2); PathExitOnFailure(hr, "Failed to append path2 to output."); } @@ -1001,15 +1015,25 @@ DAPI_(HRESULT) PathGetHierarchyArray( LPWSTR sczPathCopy = NULL; LPWSTR sczNewPathCopy = NULL; DWORD cArraySpacesNeeded = 0; + size_t cchPath = 0; + + hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); + PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath); + + if (!cchPath) + { + ExitFunction1(hr = E_INVALIDARG); + } - for (int i = 0; i < lstrlenW(wzPath); ++i) + for (size_t i = 0; i < cchPath; ++i) { if (wzPath[i] == L'\\') { ++cArraySpacesNeeded; } } - if (wzPath[lstrlenW(wzPath) - 1] != L'\\') + + if (wzPath[cchPath - 1] != L'\\') { ++cArraySpacesNeeded; } @@ -1034,10 +1058,12 @@ DAPI_(HRESULT) PathGetHierarchyArray( hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); PathExitOnFailure(hr, "Failed to copy path"); + DWORD cchPathCopy = lstrlenW(sczPathCopy); + // 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 - if (wzPath[lstrlenW(sczPathCopy) - 1] == L'\\') + if (wzPath[cchPathCopy - 1] == L'\\') { - sczPathCopy[lstrlenW(sczPathCopy) - 1] = L'\0'; + sczPathCopy[cchPathCopy - 1] = L'\0'; } hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp index afd2d089..cb617932 100644 --- a/src/dutil/regutil.cpp +++ b/src/dutil/regutil.cpp @@ -294,12 +294,15 @@ extern "C" HRESULT DAPI RegKeyEnum( { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; + SIZE_T cb = 0; DWORD cch = 0; if (psczKey && *psczKey) { - hr = StrMaxLength(*psczKey, reinterpret_cast(&cch)); + hr = StrMaxLength(*psczKey, &cb); RegExitOnFailure(hr, "Failed to determine length of string."); + + cch = (DWORD)min(DWORD_MAX, cb); } if (2 > cch) @@ -462,6 +465,7 @@ extern "C" HRESULT DAPI RegReadString( { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; + SIZE_T cbValue = 0; DWORD cch = 0; DWORD cb = 0; DWORD dwType = 0; @@ -469,8 +473,10 @@ extern "C" HRESULT DAPI RegReadString( if (psczValue && *psczValue) { - hr = StrMaxLength(*psczValue, reinterpret_cast(&cch)); + hr = StrMaxLength(*psczValue, &cbValue); RegExitOnFailure(hr, "Failed to determine length of string."); + + cch = (DWORD)min(DWORD_MAX, cbValue); } if (2 > cch) @@ -1000,18 +1006,18 @@ static HRESULT WriteStringToRegistry( __in_z_opt LPCWSTR wzName, __in_z_opt LPCWSTR wzValue, __in DWORD dwType -) + ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; - DWORD cbValue = 0; + size_t cbValue = 0; if (wzValue) { - hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), reinterpret_cast(&cbValue)); + hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue); RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); - er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), cbValue); + er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), static_cast(cbValue)); RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); } else diff --git a/src/dutil/reswutil.cpp b/src/dutil/reswutil.cpp index 42b49c55..e78de84a 100644 --- a/src/dutil/reswutil.cpp +++ b/src/dutil/reswutil.cpp @@ -288,13 +288,16 @@ static HRESULT StringBlockChangeString( { HRESULT hr = S_OK; LPWSTR pwzData = NULL; - DWORD cchData = lstrlenW(szData); + size_t cchData = 0; + + hr = ::StringCchLengthW(szData, STRSAFE_MAX_LENGTH, &cchData); + ReswExitOnRootFailure(hr, "Failed to get block string length."); pwzData = static_cast(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); hr = ::StringCchCopyW(pwzData, cchData + 1, szData); - ReswExitOnFailure(hr, "Failed to copy new block string."); + ReswExitOnRootFailure(hr, "Failed to copy new block string."); ReleaseNullMem(pStrBlock->rgwz[dwStringId]); diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp index daa090c9..550d6169 100644 --- a/src/dutil/strutil.cpp +++ b/src/dutil/strutil.cpp @@ -46,7 +46,7 @@ static HRESULT AllocFormattedArgsHelper( static HRESULT StrAllocStringMapInvariant( __deref_out_z LPWSTR* pscz, __in_z LPCWSTR wzSource, - __in int cchSource, + __in SIZE_T cchSource, __in DWORD dwMapFlags ); @@ -146,7 +146,7 @@ HRESULT DAPI StrTrimCapacity( SIZE_T cchLen = 0; hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - StrExitOnFailure(hr, "Failed to calculate length of string"); + StrExitOnRootFailure(hr, "Failed to calculate length of string"); ++cchLen; // Add 1 for null-terminator @@ -170,7 +170,7 @@ HRESULT DAPI StrTrimWhitespace( ) { HRESULT hr = S_OK; - int i = 0; + size_t i = 0; LPWSTR sczResult = NULL; // Ignore beginning whitespace @@ -179,7 +179,9 @@ HRESULT DAPI StrTrimWhitespace( wzSource++; } - i = lstrlenW(wzSource); + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &i); + StrExitOnRootFailure(hr, "Failed to get length of string"); + // Overwrite ending whitespace with null characters if (0 < i) { @@ -291,7 +293,7 @@ HRESULT DAPI StrAnsiTrimWhitespace( ) { HRESULT hr = S_OK; - int i = 0; + size_t i = 0; LPSTR sczResult = NULL; // Ignore beginning whitespace @@ -300,7 +302,9 @@ HRESULT DAPI StrAnsiTrimWhitespace( szSource++; } - i = lstrlen(szSource); + hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, &i); + StrExitOnRootFailure(hr, "Failed to get length of string"); + // Overwrite ending whitespace with null characters if (0 < i) { @@ -395,14 +399,15 @@ static HRESULT AllocStringHelper( cch /= sizeof(WCHAR); //convert the count in bytes to count in characters } - if (0 == cchSource) + if (0 == cchSource && wzSource) { - cchSource = lstrlenW(wzSource); + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "failed to get length of source string"); } SIZE_T cchNeeded; hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - StrExitOnFailure(hr, "source string is too long"); + StrExitOnRootFailure(hr, "source string is too long"); if (cch < cchNeeded) { @@ -604,19 +609,20 @@ HRESULT DAPI StrAnsiAllocStringAnsi( if (-1 == cch) { hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnRootFailure(hr, "failed to get size of destination string"); } cch /= sizeof(CHAR); //convert the count in bytes to count in characters } - if (0 == cchSource) + if (0 == cchSource && szSource) { - cchSource = lstrlenA(szSource); + hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "failed to get length of source string"); } SIZE_T cchNeeded; hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - StrExitOnFailure(hr, "source string is too long"); + StrExitOnRootFailure(hr, "source string is too long"); if (cch < cchNeeded) { @@ -1075,7 +1081,7 @@ static HRESULT AllocFormattedArgsHelper( SIZE_T cch = 0; LPWSTR pwzOriginal = NULL; SIZE_T cbOriginal = 0; - SIZE_T cchOriginal = 0; + size_t cchOriginal = 0; if (*ppwz) { @@ -1083,11 +1089,13 @@ static HRESULT AllocFormattedArgsHelper( if (-1 == cbOriginal) { hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnRootFailure(hr, "failed to get size of destination string"); } cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters - cchOriginal = lstrlenW(*ppwz); + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, &cchOriginal); + StrExitOnRootFailure(hr, "failed to get length of original string"); } if (0 == cch) // if there is no space in the string buffer @@ -1124,7 +1132,7 @@ static HRESULT AllocFormattedArgsHelper( hr = S_FALSE; } } while (S_FALSE == hr); - StrExitOnFailure(hr, "failed to format string"); + StrExitOnRootFailure(hr, "failed to format string"); LExit: if (pwzOriginal && fZeroOnRealloc) @@ -1155,7 +1163,7 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( HRESULT hr = S_OK; SIZE_T cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; LPSTR pszOriginal = NULL; - DWORD cchOriginal = 0; + size_t cchOriginal = 0; if (*ppsz) { @@ -1163,11 +1171,12 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( if (-1 == cch) { hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); + StrExitOnRootFailure(hr, "failed to get size of destination string"); } cch /= sizeof(CHAR); //convert the count in bytes to count in characters - cchOriginal = lstrlenA(*ppsz); + hr = ::StringCchLengthA(*ppsz, STRSAFE_MAX_CCH, &cchOriginal); + StrExitOnRootFailure(hr, "failed to get length of original string"); } if (0 == cch) // if there is no space in the string buffer @@ -1202,7 +1211,7 @@ extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( hr = S_FALSE; } } while (S_FALSE == hr); - StrExitOnFailure(hr, "failed to format string"); + StrExitOnRootFailure(hr, "failed to format string"); LExit: ReleaseStr(pszOriginal); @@ -1375,6 +1384,8 @@ extern "C" HRESULT DAPI StrReplaceString( HRESULT hr = S_FALSE; LPCWSTR wzSubLocation = NULL; LPWSTR pwzBuffer = NULL; + size_t cchOldSubString = 0; + size_t cchNewSubString = 0; if (!*ppwzOriginal) { @@ -1387,8 +1398,20 @@ extern "C" HRESULT DAPI StrReplaceString( ExitFunction(); } + if (wzOldSubString) + { + hr = ::StringCchLengthW(wzOldSubString, STRSAFE_MAX_CCH, &cchOldSubString); + StrExitOnRootFailure(hr, "Failed to get old string length."); + } + + if (wzNewSubString) + { + hr = ::StringCchLengthW(wzNewSubString, STRSAFE_MAX_CCH, &cchNewSubString); + StrExitOnRootFailure(hr, "Failed to get new string length."); + } + hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex); - StrExitOnFailure(hr, "Failed to diff pointers."); + StrExitOnRootFailure(hr, "Failed to diff pointers."); hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal); StrExitOnFailure(hr, "Failed to duplicate string."); @@ -1398,14 +1421,14 @@ extern "C" HRESULT DAPI StrReplaceString( hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0); StrExitOnFailure(hr, "Failed to append new string."); - hr = StrAllocConcat(&pwzBuffer, wzSubLocation + wcslen(wzOldSubString), 0); + hr = StrAllocConcat(&pwzBuffer, wzSubLocation + cchOldSubString, 0); StrExitOnFailure(hr, "Failed to append post string."); hr = StrFree(*ppwzOriginal); StrExitOnFailure(hr, "Failed to free original string."); *ppwzOriginal = pwzBuffer; - *pdwStartIndex = *pdwStartIndex + static_cast(wcslen(wzNewSubString)); + *pdwStartIndex = *pdwStartIndex + static_cast(cchNewSubString); hr = S_OK; LExit: @@ -1516,15 +1539,18 @@ extern "C" HRESULT DAPI StrHexDecode( Assert(wzSource && pbDest); HRESULT hr = S_OK; - DWORD cchSource = lstrlenW(wzSource); - DWORD i; - BYTE b; + size_t cchSource = 0; + size_t i = 0; + BYTE b = 0; + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); + StrExitOnRootFailure(hr, "Failed to get length of hex string: %ls", wzSource); Assert(0 == cchSource % 2); if (cbDest < cchSource / 2) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %u into %u bytes.", wzSource, cchSource, cbDest); + StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %Iu into %Iu bytes.", wzSource, cchSource, cbDest); } for (i = 0; i < cchSource / 2; ++i) @@ -1728,17 +1754,20 @@ extern "C" HRESULT DAPI StrAllocBase85Decode( ) { HRESULT hr = S_OK; - SIZE_T cchSource = lstrlenW(wzSource); + size_t cchSource = 0; DWORD_PTR i, n, k; - BYTE* pbDest; - SIZE_T cbDest; + BYTE* pbDest = 0; + SIZE_T cbDest = 0; if (!wzSource || !ppbDest || !pcbDest) { - return E_INVALIDARG; + ExitFunction1(hr = E_INVALIDARG); } + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); + StrExitOnRootFailure(hr, "failed to get length of base 85 string: %ls", wzSource); + // evaluate size of output and check it k = cchSource / 5; cbDest = k << 2; @@ -1932,7 +1961,8 @@ extern "C" HRESULT DAPI MultiSzPrepend( StrExitOnFailure(hr, "failed to get length of multisz"); } - cchInsert = lstrlenW(pwzInsert); + hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchInsert)); + StrExitOnRootFailure(hr, "failed to get length of insert string"); cchResult = cchInsert + cchMultiSz + 1; @@ -1942,7 +1972,7 @@ extern "C" HRESULT DAPI MultiSzPrepend( // Prepend hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert); - StrExitOnFailure(hr, "failed to copy prepend string: %ls", pwzInsert); + StrExitOnRootFailure(hr, "failed to copy prepend string: %ls", pwzInsert); // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in if (0 == cchMultiSz) @@ -2207,9 +2237,12 @@ extern "C" HRESULT DAPI MultiSzInsertString( SIZE_T cchProgress = 0; LPWSTR pwzResult = NULL; SIZE_T cchResult = 0; - SIZE_T cchString = lstrlenW(pwzInsert); + SIZE_T cchString = 0; SIZE_T cchMultiSz = 0; + hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchString)); + StrExitOnRootFailure(hr, "failed to get length of insert string"); + if (pcchMultiSz && 0 != *pcchMultiSz) { cchMultiSz = *pcchMultiSz; @@ -2464,11 +2497,15 @@ extern "C" HRESULT DAPI StrStringToInt64( INT iSign = 1; INT nDigit = 0; LARGE_INTEGER liValue = { }; + size_t cchString = 0; // get string length if not provided if (0 >= cchIn) { - cchIn = lstrlenW(wzIn); + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); + StrExitOnRootFailure(hr, "Failed to get length of string."); + + cchIn = (DWORD)cchString; if (0 >= cchIn) { ExitFunction1(hr = E_INVALIDARG); @@ -2524,11 +2561,15 @@ extern "C" HRESULT DAPI StrStringToUInt64( DWORD nDigit = 0; ULONGLONG ullValue = 0; ULONGLONG ull = 0; + size_t cchString = 0; // get string length if not provided if (0 >= cchIn) { - cchIn = lstrlenW(wzIn); + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); + StrExitOnRootFailure(hr, "Failed to get length of string."); + + cchIn = (DWORD)cchString; if (0 >= cchIn) { ExitFunction1(hr = E_INVALIDARG); @@ -2588,7 +2629,7 @@ StrAllocStringToUpperInvariant - creates an upper-case copy of a string. extern "C" HRESULT DAPI StrAllocStringToUpperInvariant( __deref_out_z LPWSTR* pscz, __in_z LPCWSTR wzSource, - __in int cchSource + __in SIZE_T cchSource ) { return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE); @@ -2601,7 +2642,7 @@ StrAllocStringToLowerInvariant - creates an lower-case copy of a string. extern "C" HRESULT DAPI StrAllocStringToLowerInvariant( __deref_out_z LPWSTR* pscz, __in_z LPCWSTR wzSource, - __in int cchSource + __in SIZE_T cchSource ) { return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE); @@ -2704,7 +2745,7 @@ Note: Assumes source and destination buffers will be the same. static HRESULT StrAllocStringMapInvariant( __deref_out_z LPWSTR* pscz, __in_z LPCWSTR wzSource, - __in int cchSource, + __in SIZE_T cchSource, __in DWORD dwMapFlags ) { @@ -2718,11 +2759,15 @@ static HRESULT StrAllocStringMapInvariant( // Need the actual string size for LCMapString. This includes the null-terminator // but LCMapString doesn't care either way. hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast(&cchSource)); - StrExitOnFailure(hr, "Failed to get the length of the string."); + StrExitOnRootFailure(hr, "Failed to get the length of the string."); + } + else if (INT_MAX < cchSource) + { + StrExitOnRootFailure(hr = E_INVALIDARG, "Source string is too long: %Iu", cchSource); } // Convert the copy of the string to upper or lower case in-place. - if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, cchSource, *pscz, cchSource)) + if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, static_cast(cchSource), *pscz, static_cast(cchSource))) { StrExitWithLastError(hr, "Failed to convert the string case."); } diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index d87e997d..d200a0ea 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -1519,13 +1519,16 @@ DAPI_(HRESULT) ThemeGetTextControl( { HRESULT hr = S_OK; HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + SIZE_T cbSize = 0; DWORD cchText = 0; DWORD cchTextRead = 0; // Ensure the string has room for at least one character. - hr = StrMaxLength(*psczText, reinterpret_cast(&cchText)); + hr = StrMaxLength(*psczText, &cbSize); ThmExitOnFailure(hr, "Failed to get text buffer length."); + cchText = (DWORD)min(DWORD_MAX, cbSize); + if (!cchText) { cchText = GROW_WINDOW_TEXT; diff --git a/src/dutil/verutil.cpp b/src/dutil/verutil.cpp index fdb5a10a..21626f94 100644 --- a/src/dutil/verutil.cpp +++ b/src/dutil/verutil.cpp @@ -244,7 +244,7 @@ DAPI_(void) VerFreeVersion( DAPI_(HRESULT) VerParseVersion( __in_z LPCWSTR wzVersion, - __in DWORD cchVersion, + __in SIZE_T cchVersion, __in BOOL fStrict, __out VERUTIL_VERSION** ppVersion ) @@ -267,9 +267,14 @@ DAPI_(HRESULT) VerParseVersion( } // Get string length if not provided. - if (0 == cchVersion) + if (!cchVersion) { - cchVersion = lstrlenW(wzVersion); + hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); + VerExitOnRootFailure(hr, "Failed to get length of version string: %ls", wzVersion); + } + else if (INT_MAX < cchVersion) + { + VerExitOnRootFailure(hr = E_INVALIDARG, "Version string is too long: %Iu", cchVersion); } if (L'v' == *wzVersion || L'V' == *wzVersion) diff --git a/src/dutil/xmlutil.cpp b/src/dutil/xmlutil.cpp index 6ecd2449..0f1e611d 100644 --- a/src/dutil/xmlutil.cpp +++ b/src/dutil/xmlutil.cpp @@ -1305,7 +1305,7 @@ extern "C" HRESULT DAPI XmlSaveDocumentToBuffer( XmlExitOnFailure(hr, "Failed to get stream size."); // allocate buffer - pbDest = static_cast(MemAlloc((SIZE_T)statstg.cbSize.LowPart, TRUE)); + pbDest = static_cast(MemAlloc(statstg.cbSize.LowPart, TRUE)); XmlExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); // read data from stream diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp index 0087a1d5..ac071ef2 100644 --- a/src/test/DUtilUnitTest/FileUtilTest.cpp +++ b/src/test/DUtilUnitTest/FileUtilTest.cpp @@ -50,7 +50,7 @@ namespace DutilTests } private: - void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, DWORD dwExpectedStringLength, FILE_ENCODING feExpectedEncoding) + void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, size_t cbExpectedStringLength, FILE_ENCODING feExpectedEncoding) { HRESULT hr = S_OK; LPWSTR sczFullPath = NULL; @@ -61,6 +61,7 @@ namespace DutilTests DWORD cbFile1 = 0; BYTE *pbFile2 = NULL; DWORD cbFile2 = 0; + size_t cbActualStringLength = 0; try { @@ -77,10 +78,13 @@ namespace DutilTests NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath); } - if ((DWORD)lstrlenW(sczContents) != dwExpectedStringLength) + hr = ::StringCchLengthW(sczContents, STRSAFE_MAX_CCH, &cbActualStringLength); + NativeAssert::Succeeded(hr, "Failed to get length of text from file: {0}", sczFullPath); + + if (cbActualStringLength != cbExpectedStringLength) { hr = E_FAIL; - ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %u, found size %u)", sczFullPath, dwExpectedStringLength, lstrlenW(sczContents)); + ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %Iu, found size %Iu)", sczFullPath, cbExpectedStringLength, cbActualStringLength); } if (feEncodingFound != feExpectedEncoding) -- cgit v1.2.3-55-g6feb From f39e7a3e164d0736e45049e5726d0da2013da3c9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 06:31:03 -0700 Subject: Update to latest build infrastructure --- .gitignore | 43 +++++++--- appveyor.cmd | 22 ++--- signing.json | 13 +++ src/Cpp.Build.props | 108 ------------------------- src/Directory.Build.props | 2 +- src/Directory.Build.targets | 87 ++++++++++++-------- src/Directory.csproj.props | 13 +++ src/Directory.vcxproj.props | 115 +++++++++++++++++++++++++++ src/dutil/dutil.vcxproj | 21 ++--- src/dutil/packages.config | 3 + src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 11 ++- 11 files changed, 261 insertions(+), 177 deletions(-) create mode 100644 signing.json delete mode 100644 src/Cpp.Build.props create mode 100644 src/Directory.csproj.props create mode 100644 src/Directory.vcxproj.props diff --git a/.gitignore b/.gitignore index 3e8a1553..3a8542dc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -20,12 +23,14 @@ [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ @@ -39,9 +44,10 @@ Generated\ Files/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ @@ -56,6 +62,9 @@ project.lock.json project.fragment.lock.json artifacts/ +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -122,9 +131,6 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in -.JustCode - # TeamCity is a build add-in _TeamCity* @@ -135,6 +141,11 @@ _TeamCity* .axoCover/* !.axoCover/settings.json +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml @@ -182,6 +193,8 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. @@ -206,6 +219,8 @@ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored @@ -231,8 +246,6 @@ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ -# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true -**/wwwroot/lib/ # RIA/Silverlight projects Generated_Code/ @@ -257,6 +270,9 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -292,10 +308,6 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - # CodeRush personal settings .cr/personal @@ -337,5 +349,14 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ -# BeatPulse healthcheck temp database +# BeatPulse healthcheck temp database healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/appveyor.cmd b/appveyor.cmd index 9b6bc112..85476b8e 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -1,22 +1,24 @@ @setlocal @pushd %~dp0 +@set _C=Release +@if /i "%1"=="debug" set _C=Debug nuget restore || exit /b -msbuild -t:Test -p:Configuration=Release src\test\DUtilUnitTest || exit /b +msbuild -t:Test -p:Configuration=%_C% src\test\DUtilUnitTest || exit /b -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140 || exit /b -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 || exit /b +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v140 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v140 || exit /b -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141 || exit /b -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141 || exit /b -msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v141 || exit /b -msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 || exit /b -msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 || exit /b -msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v142 || exit /b -msbuild -p:Configuration=Release -t:PackNativeNuget src\dutil\dutil.vcxproj || exit /b +msbuild -p:Configuration=%_C% -t:PackNative src\dutil\dutil.vcxproj || exit /b @popd @endlocal \ No newline at end of file diff --git a/signing.json b/signing.json new file mode 100644 index 00000000..fe1c8c9b --- /dev/null +++ b/signing.json @@ -0,0 +1,13 @@ +{ + "SignClient": { + "AzureAd": { + "AADInstance": "https://login.microsoftonline.com/", + "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", + "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" + }, + "Service": { + "Url": "https://codesign.dotnetfoundation.org/", + "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" + } + } +} diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props deleted file mode 100644 index fb805b42..00000000 --- a/src/Cpp.Build.props +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - Win32 - $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ - $(OutputPath)$(Platform)\ - - - - $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) - - - - $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset - - - - - $(DisableSpecificCompilerWarnings) - Level4 - $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) - Use - precomp.h - StdCall - true - false - -YlprecompDefine - /Zc:threadSafeInit- %(AdditionalOptions) - true - - - $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) - $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) - - - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) - - - $(ProjectSubSystem) - $(ProjectModuleDefinitionFile) - $(ResourceOnlyDll) - true - $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) - /IGNORE:4099 %(AdditionalOptions) - - - - - - NoExtensions - - - - - CDecl - - - - - OldStyle - true - true - - - - - Disabled - EnableFastChecks - _DEBUG;DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - - MultiThreadedDebugDll - - - - - MinSpace - NDEBUG;%(PreprocessorDefinitions) - true - true - MultiThreaded - - - true - true - - - - - - MultiThreadedDll - - - - - $(LinkKeyFile) - $(LinkDelaySign) - - - diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e853e22d..fb34d54e 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -21,6 +21,6 @@ WiX Toolset - + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index dac7452a..44701fb6 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -4,45 +4,70 @@ Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.targets then update all of the repos. --> - - true - $(SolutionPath) - $(NCrunchOriginalSolutionPath) + $(BaseOutputPath)obj\.tools + $(SigningToolFolder)\SignClient.exe + $(SigningToolFolder)\empty-filelist.txt + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json + + + + false + $(OutputPath)\$(AssemblyName).xml - - + + + $(PrivateRepositoryUrl.Replace('.git','')) + + $(MSBuildProjectName).nuspec + $(MSBuildProjectDirectory) + $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" + $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) + true + snupkg + + + + + + + + + + + + + + + - - $([System.IO.File]::ReadAllText($(TheSolutionPath))) - $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) - (?<="[PackageName]", ")(.*)(?=", ") - + + - - - - %(Identity) - $(SolutionFileContent.Contains('\%(Identity).csproj')) - + - - - $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) - $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) - + + - + + + + - - - + + - - + + diff --git a/src/Directory.csproj.props b/src/Directory.csproj.props new file mode 100644 index 00000000..81d24ad1 --- /dev/null +++ b/src/Directory.csproj.props @@ -0,0 +1,13 @@ + + + + + true + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + false + + diff --git a/src/Directory.vcxproj.props b/src/Directory.vcxproj.props new file mode 100644 index 00000000..9ea7071b --- /dev/null +++ b/src/Directory.vcxproj.props @@ -0,0 +1,115 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + $(Company) + $(Copyright) + + win-x86;win-x64;win-arm64 + native,Version=v0.0 + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index b84fd80f..4e341438 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -37,15 +37,12 @@ v142 MultiByte WiX Toolset native library foundation + WixToolset.DUtil - - - - @@ -177,16 +174,10 @@ - - - + + + + - - - 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}. - - - - \ No newline at end of file + diff --git a/src/dutil/packages.config b/src/dutil/packages.config index 1ffaa8df..5bbcd994 100644 --- a/src/dutil/packages.config +++ b/src/dutil/packages.config @@ -1,4 +1,7 @@  + + + \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj index 32463262..18410e5d 100644 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -1,7 +1,6 @@ - @@ -14,6 +13,7 @@ Win32 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} {AB7EE608-E5FB-42A5-831F-0DEEEA141223} @@ -22,13 +22,17 @@ DynamicLibrary Unicode true + false + + ..\..\dutil\inc rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib + @@ -52,17 +56,21 @@ + + + + @@ -73,6 +81,7 @@ ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll + {1244E671-F108-4334-BA52-8A7517F26ECD} -- cgit v1.2.3-55-g6feb From 7f642e51670bc38a4ef782a363936850bc2b0ba9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 06:38:23 -0700 Subject: Move dutil into libs/dutil --- .editorconfig | 37 - README.md | 2 - appveyor.cmd | 24 - appveyor.yml | 44 - dutil.sln | 47 - nuget.config | 8 - signing.json | 13 - src/.editorconfig | 37 + src/CustomizedNativeRecommendedRules.ruleset | 8 - src/Directory.Build.props | 26 - src/Directory.Build.targets | 73 - src/Directory.csproj.props | 13 - src/Directory.vcxproj.props | 115 - src/NativeMultiTargeting.Build.props | 10 - src/dutil/acl2util.cpp | 135 - src/dutil/aclutil.cpp | 1044 ---- src/dutil/apputil.cpp | 124 - src/dutil/apuputil.cpp | 700 --- src/dutil/atomutil.cpp | 1297 ----- src/dutil/buffutil.cpp | 529 -- src/dutil/build/WixToolset.DUtil.props | 28 - src/dutil/butil.cpp | 257 - src/dutil/cabcutil.cpp | 1539 ------ src/dutil/cabutil.cpp | 617 --- src/dutil/certutil.cpp | 342 -- src/dutil/conutil.cpp | 673 --- src/dutil/cryputil.cpp | 404 -- src/dutil/deputil.cpp | 699 --- src/dutil/dictutil.cpp | 784 --- src/dutil/dirutil.cpp | 441 -- src/dutil/dlutil.cpp | 802 --- src/dutil/dpiutil.cpp | 274 - src/dutil/dutil.cpp | 524 -- src/dutil/dutil.nuspec | 27 - src/dutil/dutil.vcxproj | 183 - src/dutil/dutil.vcxproj.filters | 372 -- src/dutil/eseutil.cpp | 1340 ----- src/dutil/fileutil.cpp | 2032 ------- src/dutil/gdiputil.cpp | 227 - src/dutil/guidutil.cpp | 54 - src/dutil/iis7util.cpp | 535 -- src/dutil/inc/aclutil.h | 154 - src/dutil/inc/apputil.h | 45 - src/dutil/inc/apuputil.h | 87 - src/dutil/inc/atomutil.h | 146 - src/dutil/inc/buffutil.h | 91 - src/dutil/inc/butil.h | 60 - src/dutil/inc/cabcutil.h | 62 - src/dutil/inc/cabutil.h | 56 - src/dutil/inc/certutil.h | 66 - src/dutil/inc/conutil.h | 80 - src/dutil/inc/cryputil.h | 106 - src/dutil/inc/deputil.h | 147 - src/dutil/inc/dictutil.h | 69 - src/dutil/inc/dirutil.h | 59 - src/dutil/inc/dlutil.h | 59 - src/dutil/inc/dpiutil.h | 120 - src/dutil/inc/dutil.h | 190 - src/dutil/inc/dutilsources.h | 76 - src/dutil/inc/eseutil.h | 223 - src/dutil/inc/fileutil.h | 247 - src/dutil/inc/gdiputil.h | 39 - src/dutil/inc/guidutil.h | 21 - src/dutil/inc/iis7util.h | 222 - src/dutil/inc/inetutil.h | 39 - src/dutil/inc/iniutil.h | 79 - src/dutil/inc/jsonutil.h | 112 - src/dutil/inc/locutil.h | 120 - src/dutil/inc/logutil.h | 192 - src/dutil/inc/memutil.h | 80 - src/dutil/inc/metautil.h | 52 - src/dutil/inc/monutil.h | 108 - src/dutil/inc/osutil.h | 42 - src/dutil/inc/pathutil.h | 252 - src/dutil/inc/perfutil.h | 24 - src/dutil/inc/polcutil.h | 39 - src/dutil/inc/procutil.h | 75 - src/dutil/inc/regutil.h | 246 - src/dutil/inc/resrutil.h | 43 - src/dutil/inc/reswutil.h | 31 - src/dutil/inc/rexutil.h | 54 - src/dutil/inc/rmutil.h | 46 - src/dutil/inc/rssutil.h | 89 - src/dutil/inc/sceutil.h | 273 - src/dutil/inc/sczutil.h | 30 - src/dutil/inc/shelutil.h | 47 - src/dutil/inc/sqlutil.h | 136 - src/dutil/inc/srputil.h | 45 - src/dutil/inc/strutil.h | 316 -- src/dutil/inc/svcutil.h | 21 - src/dutil/inc/thmutil.h | 765 --- src/dutil/inc/timeutil.h | 38 - src/dutil/inc/uncutil.h | 20 - src/dutil/inc/uriutil.h | 100 - src/dutil/inc/userutil.h | 32 - src/dutil/inc/verutil.h | 93 - src/dutil/inc/wiutil.h | 402 -- src/dutil/inc/wuautil.h | 19 - src/dutil/inc/xmlutil.h | 167 - src/dutil/inetutil.cpp | 155 - src/dutil/iniutil.cpp | 768 --- src/dutil/jsonutil.cpp | 687 --- src/dutil/locutil.cpp | 628 --- src/dutil/logutil.cpp | 961 ---- src/dutil/memutil.cpp | 336 -- src/dutil/metautil.cpp | 378 -- src/dutil/monutil.cpp | 2019 ------- src/dutil/osutil.cpp | 251 - src/dutil/packages.config | 7 - src/dutil/path2utl.cpp | 104 - src/dutil/pathutil.cpp | 1083 ---- src/dutil/perfutil.cpp | 82 - src/dutil/polcutil.cpp | 126 - src/dutil/precomp.h | 98 - src/dutil/proc2utl.cpp | 83 - src/dutil/proc3utl.cpp | 129 - src/dutil/procutil.cpp | 522 -- src/dutil/regutil.cpp | 1035 ---- src/dutil/resrutil.cpp | 266 - src/dutil/reswutil.cpp | 386 -- src/dutil/rexutil.cpp | 601 --- src/dutil/rmutil.cpp | 488 -- src/dutil/rssutil.cpp | 647 --- src/dutil/sceutil.cpp | 2489 --------- src/dutil/shelutil.cpp | 342 -- src/dutil/sqlutil.cpp | 1002 ---- src/dutil/srputil.cpp | 252 - src/dutil/strutil.cpp | 2824 ---------- src/dutil/svcutil.cpp | 59 - src/dutil/thmutil.cpp | 5709 -------------------- src/dutil/timeutil.cpp | 385 -- src/dutil/uncutil.cpp | 69 - src/dutil/uriutil.cpp | 553 -- src/dutil/userutil.cpp | 300 - src/dutil/verutil.cpp | 647 --- src/dutil/wiutil.cpp | 1629 ------ src/dutil/wuautil.cpp | 104 - src/dutil/xmlutil.cpp | 1332 ----- src/dutil/xsd/thmutil.xsd | 1188 ---- .../dutil/CustomizedNativeRecommendedRules.ruleset | 8 + src/libs/dutil/Directory.Build.props | 26 + src/libs/dutil/Directory.Build.targets | 73 + src/libs/dutil/Directory.csproj.props | 13 + src/libs/dutil/Directory.vcxproj.props | 115 + src/libs/dutil/NativeMultiTargeting.Build.props | 10 + src/libs/dutil/README.md | 2 + src/libs/dutil/WixToolset.DUtil/acl2util.cpp | 135 + src/libs/dutil/WixToolset.DUtil/aclutil.cpp | 1044 ++++ src/libs/dutil/WixToolset.DUtil/apputil.cpp | 124 + src/libs/dutil/WixToolset.DUtil/apuputil.cpp | 700 +++ src/libs/dutil/WixToolset.DUtil/atomutil.cpp | 1297 +++++ src/libs/dutil/WixToolset.DUtil/buffutil.cpp | 529 ++ .../WixToolset.DUtil/build/WixToolset.DUtil.props | 28 + src/libs/dutil/WixToolset.DUtil/butil.cpp | 257 + src/libs/dutil/WixToolset.DUtil/cabcutil.cpp | 1539 ++++++ src/libs/dutil/WixToolset.DUtil/cabutil.cpp | 617 +++ src/libs/dutil/WixToolset.DUtil/certutil.cpp | 342 ++ src/libs/dutil/WixToolset.DUtil/conutil.cpp | 673 +++ src/libs/dutil/WixToolset.DUtil/cryputil.cpp | 404 ++ src/libs/dutil/WixToolset.DUtil/deputil.cpp | 699 +++ src/libs/dutil/WixToolset.DUtil/dictutil.cpp | 784 +++ src/libs/dutil/WixToolset.DUtil/dirutil.cpp | 441 ++ src/libs/dutil/WixToolset.DUtil/dlutil.cpp | 802 +++ src/libs/dutil/WixToolset.DUtil/dpiutil.cpp | 274 + src/libs/dutil/WixToolset.DUtil/dutil.cpp | 524 ++ src/libs/dutil/WixToolset.DUtil/dutil.nuspec | 27 + src/libs/dutil/WixToolset.DUtil/dutil.vcxproj | 183 + .../dutil/WixToolset.DUtil/dutil.vcxproj.filters | 372 ++ src/libs/dutil/WixToolset.DUtil/eseutil.cpp | 1340 +++++ src/libs/dutil/WixToolset.DUtil/fileutil.cpp | 2032 +++++++ src/libs/dutil/WixToolset.DUtil/gdiputil.cpp | 227 + src/libs/dutil/WixToolset.DUtil/guidutil.cpp | 54 + src/libs/dutil/WixToolset.DUtil/iis7util.cpp | 535 ++ src/libs/dutil/WixToolset.DUtil/inc/aclutil.h | 154 + src/libs/dutil/WixToolset.DUtil/inc/apputil.h | 45 + src/libs/dutil/WixToolset.DUtil/inc/apuputil.h | 87 + src/libs/dutil/WixToolset.DUtil/inc/atomutil.h | 146 + src/libs/dutil/WixToolset.DUtil/inc/buffutil.h | 91 + src/libs/dutil/WixToolset.DUtil/inc/butil.h | 60 + src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h | 62 + src/libs/dutil/WixToolset.DUtil/inc/cabutil.h | 56 + src/libs/dutil/WixToolset.DUtil/inc/certutil.h | 66 + src/libs/dutil/WixToolset.DUtil/inc/conutil.h | 80 + src/libs/dutil/WixToolset.DUtil/inc/cryputil.h | 106 + src/libs/dutil/WixToolset.DUtil/inc/deputil.h | 147 + src/libs/dutil/WixToolset.DUtil/inc/dictutil.h | 69 + src/libs/dutil/WixToolset.DUtil/inc/dirutil.h | 59 + src/libs/dutil/WixToolset.DUtil/inc/dlutil.h | 59 + src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h | 120 + src/libs/dutil/WixToolset.DUtil/inc/dutil.h | 190 + src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h | 76 + src/libs/dutil/WixToolset.DUtil/inc/eseutil.h | 223 + src/libs/dutil/WixToolset.DUtil/inc/fileutil.h | 247 + src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h | 39 + src/libs/dutil/WixToolset.DUtil/inc/guidutil.h | 21 + src/libs/dutil/WixToolset.DUtil/inc/iis7util.h | 222 + src/libs/dutil/WixToolset.DUtil/inc/inetutil.h | 39 + src/libs/dutil/WixToolset.DUtil/inc/iniutil.h | 79 + src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h | 112 + src/libs/dutil/WixToolset.DUtil/inc/locutil.h | 120 + src/libs/dutil/WixToolset.DUtil/inc/logutil.h | 192 + src/libs/dutil/WixToolset.DUtil/inc/memutil.h | 80 + src/libs/dutil/WixToolset.DUtil/inc/metautil.h | 52 + src/libs/dutil/WixToolset.DUtil/inc/monutil.h | 108 + src/libs/dutil/WixToolset.DUtil/inc/osutil.h | 42 + src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 252 + src/libs/dutil/WixToolset.DUtil/inc/perfutil.h | 24 + src/libs/dutil/WixToolset.DUtil/inc/polcutil.h | 39 + src/libs/dutil/WixToolset.DUtil/inc/procutil.h | 75 + src/libs/dutil/WixToolset.DUtil/inc/regutil.h | 246 + src/libs/dutil/WixToolset.DUtil/inc/resrutil.h | 43 + src/libs/dutil/WixToolset.DUtil/inc/reswutil.h | 31 + src/libs/dutil/WixToolset.DUtil/inc/rexutil.h | 54 + src/libs/dutil/WixToolset.DUtil/inc/rmutil.h | 46 + src/libs/dutil/WixToolset.DUtil/inc/rssutil.h | 89 + src/libs/dutil/WixToolset.DUtil/inc/sceutil.h | 273 + src/libs/dutil/WixToolset.DUtil/inc/sczutil.h | 30 + src/libs/dutil/WixToolset.DUtil/inc/shelutil.h | 47 + src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h | 136 + src/libs/dutil/WixToolset.DUtil/inc/srputil.h | 45 + src/libs/dutil/WixToolset.DUtil/inc/strutil.h | 316 ++ src/libs/dutil/WixToolset.DUtil/inc/svcutil.h | 21 + src/libs/dutil/WixToolset.DUtil/inc/thmutil.h | 765 +++ src/libs/dutil/WixToolset.DUtil/inc/timeutil.h | 38 + src/libs/dutil/WixToolset.DUtil/inc/uncutil.h | 20 + src/libs/dutil/WixToolset.DUtil/inc/uriutil.h | 100 + src/libs/dutil/WixToolset.DUtil/inc/userutil.h | 32 + src/libs/dutil/WixToolset.DUtil/inc/verutil.h | 93 + src/libs/dutil/WixToolset.DUtil/inc/wiutil.h | 402 ++ src/libs/dutil/WixToolset.DUtil/inc/wuautil.h | 19 + src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h | 167 + src/libs/dutil/WixToolset.DUtil/inetutil.cpp | 155 + src/libs/dutil/WixToolset.DUtil/iniutil.cpp | 768 +++ src/libs/dutil/WixToolset.DUtil/jsonutil.cpp | 687 +++ src/libs/dutil/WixToolset.DUtil/locutil.cpp | 628 +++ src/libs/dutil/WixToolset.DUtil/logutil.cpp | 961 ++++ src/libs/dutil/WixToolset.DUtil/memutil.cpp | 336 ++ src/libs/dutil/WixToolset.DUtil/metautil.cpp | 378 ++ src/libs/dutil/WixToolset.DUtil/monutil.cpp | 2019 +++++++ src/libs/dutil/WixToolset.DUtil/osutil.cpp | 251 + src/libs/dutil/WixToolset.DUtil/packages.config | 7 + src/libs/dutil/WixToolset.DUtil/path2utl.cpp | 104 + src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 1083 ++++ src/libs/dutil/WixToolset.DUtil/perfutil.cpp | 82 + src/libs/dutil/WixToolset.DUtil/polcutil.cpp | 126 + src/libs/dutil/WixToolset.DUtil/precomp.h | 98 + src/libs/dutil/WixToolset.DUtil/proc2utl.cpp | 83 + src/libs/dutil/WixToolset.DUtil/proc3utl.cpp | 129 + src/libs/dutil/WixToolset.DUtil/procutil.cpp | 522 ++ src/libs/dutil/WixToolset.DUtil/regutil.cpp | 1035 ++++ src/libs/dutil/WixToolset.DUtil/resrutil.cpp | 266 + src/libs/dutil/WixToolset.DUtil/reswutil.cpp | 386 ++ src/libs/dutil/WixToolset.DUtil/rexutil.cpp | 601 +++ src/libs/dutil/WixToolset.DUtil/rmutil.cpp | 488 ++ src/libs/dutil/WixToolset.DUtil/rssutil.cpp | 647 +++ src/libs/dutil/WixToolset.DUtil/sceutil.cpp | 2489 +++++++++ src/libs/dutil/WixToolset.DUtil/shelutil.cpp | 342 ++ src/libs/dutil/WixToolset.DUtil/sqlutil.cpp | 1002 ++++ src/libs/dutil/WixToolset.DUtil/srputil.cpp | 252 + src/libs/dutil/WixToolset.DUtil/strutil.cpp | 2824 ++++++++++ src/libs/dutil/WixToolset.DUtil/svcutil.cpp | 59 + src/libs/dutil/WixToolset.DUtil/thmutil.cpp | 5709 ++++++++++++++++++++ src/libs/dutil/WixToolset.DUtil/timeutil.cpp | 385 ++ src/libs/dutil/WixToolset.DUtil/uncutil.cpp | 69 + src/libs/dutil/WixToolset.DUtil/uriutil.cpp | 553 ++ src/libs/dutil/WixToolset.DUtil/userutil.cpp | 300 + src/libs/dutil/WixToolset.DUtil/verutil.cpp | 647 +++ src/libs/dutil/WixToolset.DUtil/wiutil.cpp | 1629 ++++++ src/libs/dutil/WixToolset.DUtil/wuautil.cpp | 104 + src/libs/dutil/WixToolset.DUtil/xmlutil.cpp | 1332 +++++ src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd | 1188 ++++ src/libs/dutil/appveyor.cmd | 24 + src/libs/dutil/appveyor.yml | 44 + src/libs/dutil/dutil.sln | 47 + src/libs/dutil/nuget.config | 8 + .../dutil/test/DUtilUnitTest/ApupUtilTests.cpp | 46 + src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp | 12 + src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp | 35 + .../dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 99 + .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 80 + src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp | 191 + src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp | 70 + src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp | 125 + src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp | 60 + src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp | 345 ++ src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp | 505 ++ src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp | 487 ++ src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | 80 + src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp | 488 ++ src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp | 192 + .../TestData/ApupUtilTests/FeedBv2.0.xml | 68 + src/libs/dutil/test/DUtilUnitTest/UnitTest.rc | 6 + src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp | 98 + src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp | 933 ++++ src/libs/dutil/test/DUtilUnitTest/error.cpp | 26 + src/libs/dutil/test/DUtilUnitTest/error.h | 14 + src/libs/dutil/test/DUtilUnitTest/packages.config | 13 + src/libs/dutil/test/DUtilUnitTest/precomp.cpp | 3 + src/libs/dutil/test/DUtilUnitTest/precomp.h | 32 + src/libs/dutil/test/DUtilUnitTest/resource.h | 2 + src/signing.json | 13 + src/test/DUtilUnitTest/ApupUtilTests.cpp | 46 - src/test/DUtilUnitTest/AssemblyInfo.cpp | 12 - src/test/DUtilUnitTest/DUtilTests.cpp | 35 - src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 99 - .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 80 - src/test/DUtilUnitTest/DictUtilTest.cpp | 191 - src/test/DUtilUnitTest/DirUtilTests.cpp | 70 - src/test/DUtilUnitTest/FileUtilTest.cpp | 125 - src/test/DUtilUnitTest/GuidUtilTest.cpp | 60 - src/test/DUtilUnitTest/IniUtilTest.cpp | 345 -- src/test/DUtilUnitTest/MemUtilTest.cpp | 505 -- src/test/DUtilUnitTest/MonUtilTest.cpp | 487 -- src/test/DUtilUnitTest/PathUtilTest.cpp | 80 - src/test/DUtilUnitTest/SceUtilTest.cpp | 488 -- src/test/DUtilUnitTest/StrUtilTest.cpp | 192 - .../TestData/ApupUtilTests/FeedBv2.0.xml | 68 - src/test/DUtilUnitTest/UnitTest.rc | 6 - src/test/DUtilUnitTest/UriUtilTest.cpp | 98 - src/test/DUtilUnitTest/VerUtilTests.cpp | 933 ---- src/test/DUtilUnitTest/error.cpp | 26 - src/test/DUtilUnitTest/error.h | 14 - src/test/DUtilUnitTest/packages.config | 13 - src/test/DUtilUnitTest/precomp.cpp | 3 - src/test/DUtilUnitTest/precomp.h | 32 - src/test/DUtilUnitTest/resource.h | 2 - src/version.json | 11 + version.json | 11 - 328 files changed, 57800 insertions(+), 57800 deletions(-) delete mode 100644 .editorconfig delete mode 100644 README.md delete mode 100644 appveyor.cmd delete mode 100644 appveyor.yml delete mode 100644 dutil.sln delete mode 100644 nuget.config delete mode 100644 signing.json create mode 100644 src/.editorconfig delete mode 100644 src/CustomizedNativeRecommendedRules.ruleset delete mode 100644 src/Directory.Build.props delete mode 100644 src/Directory.Build.targets delete mode 100644 src/Directory.csproj.props delete mode 100644 src/Directory.vcxproj.props delete mode 100644 src/NativeMultiTargeting.Build.props delete mode 100644 src/dutil/acl2util.cpp delete mode 100644 src/dutil/aclutil.cpp delete mode 100644 src/dutil/apputil.cpp delete mode 100644 src/dutil/apuputil.cpp delete mode 100644 src/dutil/atomutil.cpp delete mode 100644 src/dutil/buffutil.cpp delete mode 100644 src/dutil/build/WixToolset.DUtil.props delete mode 100644 src/dutil/butil.cpp delete mode 100644 src/dutil/cabcutil.cpp delete mode 100644 src/dutil/cabutil.cpp delete mode 100644 src/dutil/certutil.cpp delete mode 100644 src/dutil/conutil.cpp delete mode 100644 src/dutil/cryputil.cpp delete mode 100644 src/dutil/deputil.cpp delete mode 100644 src/dutil/dictutil.cpp delete mode 100644 src/dutil/dirutil.cpp delete mode 100644 src/dutil/dlutil.cpp delete mode 100644 src/dutil/dpiutil.cpp delete mode 100644 src/dutil/dutil.cpp delete mode 100644 src/dutil/dutil.nuspec delete mode 100644 src/dutil/dutil.vcxproj delete mode 100644 src/dutil/dutil.vcxproj.filters delete mode 100644 src/dutil/eseutil.cpp delete mode 100644 src/dutil/fileutil.cpp delete mode 100644 src/dutil/gdiputil.cpp delete mode 100644 src/dutil/guidutil.cpp delete mode 100644 src/dutil/iis7util.cpp delete mode 100644 src/dutil/inc/aclutil.h delete mode 100644 src/dutil/inc/apputil.h delete mode 100644 src/dutil/inc/apuputil.h delete mode 100644 src/dutil/inc/atomutil.h delete mode 100644 src/dutil/inc/buffutil.h delete mode 100644 src/dutil/inc/butil.h delete mode 100644 src/dutil/inc/cabcutil.h delete mode 100644 src/dutil/inc/cabutil.h delete mode 100644 src/dutil/inc/certutil.h delete mode 100644 src/dutil/inc/conutil.h delete mode 100644 src/dutil/inc/cryputil.h delete mode 100644 src/dutil/inc/deputil.h delete mode 100644 src/dutil/inc/dictutil.h delete mode 100644 src/dutil/inc/dirutil.h delete mode 100644 src/dutil/inc/dlutil.h delete mode 100644 src/dutil/inc/dpiutil.h delete mode 100644 src/dutil/inc/dutil.h delete mode 100644 src/dutil/inc/dutilsources.h delete mode 100644 src/dutil/inc/eseutil.h delete mode 100644 src/dutil/inc/fileutil.h delete mode 100644 src/dutil/inc/gdiputil.h delete mode 100644 src/dutil/inc/guidutil.h delete mode 100644 src/dutil/inc/iis7util.h delete mode 100644 src/dutil/inc/inetutil.h delete mode 100644 src/dutil/inc/iniutil.h delete mode 100644 src/dutil/inc/jsonutil.h delete mode 100644 src/dutil/inc/locutil.h delete mode 100644 src/dutil/inc/logutil.h delete mode 100644 src/dutil/inc/memutil.h delete mode 100644 src/dutil/inc/metautil.h delete mode 100644 src/dutil/inc/monutil.h delete mode 100644 src/dutil/inc/osutil.h delete mode 100644 src/dutil/inc/pathutil.h delete mode 100644 src/dutil/inc/perfutil.h delete mode 100644 src/dutil/inc/polcutil.h delete mode 100644 src/dutil/inc/procutil.h delete mode 100644 src/dutil/inc/regutil.h delete mode 100644 src/dutil/inc/resrutil.h delete mode 100644 src/dutil/inc/reswutil.h delete mode 100644 src/dutil/inc/rexutil.h delete mode 100644 src/dutil/inc/rmutil.h delete mode 100644 src/dutil/inc/rssutil.h delete mode 100644 src/dutil/inc/sceutil.h delete mode 100644 src/dutil/inc/sczutil.h delete mode 100644 src/dutil/inc/shelutil.h delete mode 100644 src/dutil/inc/sqlutil.h delete mode 100644 src/dutil/inc/srputil.h delete mode 100644 src/dutil/inc/strutil.h delete mode 100644 src/dutil/inc/svcutil.h delete mode 100644 src/dutil/inc/thmutil.h delete mode 100644 src/dutil/inc/timeutil.h delete mode 100644 src/dutil/inc/uncutil.h delete mode 100644 src/dutil/inc/uriutil.h delete mode 100644 src/dutil/inc/userutil.h delete mode 100644 src/dutil/inc/verutil.h delete mode 100644 src/dutil/inc/wiutil.h delete mode 100644 src/dutil/inc/wuautil.h delete mode 100644 src/dutil/inc/xmlutil.h delete mode 100644 src/dutil/inetutil.cpp delete mode 100644 src/dutil/iniutil.cpp delete mode 100644 src/dutil/jsonutil.cpp delete mode 100644 src/dutil/locutil.cpp delete mode 100644 src/dutil/logutil.cpp delete mode 100644 src/dutil/memutil.cpp delete mode 100644 src/dutil/metautil.cpp delete mode 100644 src/dutil/monutil.cpp delete mode 100644 src/dutil/osutil.cpp delete mode 100644 src/dutil/packages.config delete mode 100644 src/dutil/path2utl.cpp delete mode 100644 src/dutil/pathutil.cpp delete mode 100644 src/dutil/perfutil.cpp delete mode 100644 src/dutil/polcutil.cpp delete mode 100644 src/dutil/precomp.h delete mode 100644 src/dutil/proc2utl.cpp delete mode 100644 src/dutil/proc3utl.cpp delete mode 100644 src/dutil/procutil.cpp delete mode 100644 src/dutil/regutil.cpp delete mode 100644 src/dutil/resrutil.cpp delete mode 100644 src/dutil/reswutil.cpp delete mode 100644 src/dutil/rexutil.cpp delete mode 100644 src/dutil/rmutil.cpp delete mode 100644 src/dutil/rssutil.cpp delete mode 100644 src/dutil/sceutil.cpp delete mode 100644 src/dutil/shelutil.cpp delete mode 100644 src/dutil/sqlutil.cpp delete mode 100644 src/dutil/srputil.cpp delete mode 100644 src/dutil/strutil.cpp delete mode 100644 src/dutil/svcutil.cpp delete mode 100644 src/dutil/thmutil.cpp delete mode 100644 src/dutil/timeutil.cpp delete mode 100644 src/dutil/uncutil.cpp delete mode 100644 src/dutil/uriutil.cpp delete mode 100644 src/dutil/userutil.cpp delete mode 100644 src/dutil/verutil.cpp delete mode 100644 src/dutil/wiutil.cpp delete mode 100644 src/dutil/wuautil.cpp delete mode 100644 src/dutil/xmlutil.cpp delete mode 100644 src/dutil/xsd/thmutil.xsd create mode 100644 src/libs/dutil/CustomizedNativeRecommendedRules.ruleset create mode 100644 src/libs/dutil/Directory.Build.props create mode 100644 src/libs/dutil/Directory.Build.targets create mode 100644 src/libs/dutil/Directory.csproj.props create mode 100644 src/libs/dutil/Directory.vcxproj.props create mode 100644 src/libs/dutil/NativeMultiTargeting.Build.props create mode 100644 src/libs/dutil/README.md create mode 100644 src/libs/dutil/WixToolset.DUtil/acl2util.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/aclutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/apputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/apuputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/atomutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/buffutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props create mode 100644 src/libs/dutil/WixToolset.DUtil/butil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/cabcutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/cabutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/certutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/conutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/cryputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/deputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dictutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dirutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dlutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dpiutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.nuspec create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.vcxproj create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters create mode 100644 src/libs/dutil/WixToolset.DUtil/eseutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/fileutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/gdiputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/guidutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/iis7util.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/aclutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/apputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/apuputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/atomutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/buffutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/butil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/cabutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/certutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/conutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/cryputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/deputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dictutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dirutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dlutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/eseutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/fileutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/guidutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/iis7util.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/inetutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/iniutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/locutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/logutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/memutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/metautil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/monutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/osutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/pathutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/perfutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/polcutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/procutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/regutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/resrutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/reswutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/rexutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/rmutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/rssutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/sceutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/sczutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/shelutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/srputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/strutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/svcutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/thmutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/timeutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/uncutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/uriutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/userutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/verutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/wiutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/wuautil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inetutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/iniutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/jsonutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/locutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/logutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/memutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/metautil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/monutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/osutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/packages.config create mode 100644 src/libs/dutil/WixToolset.DUtil/path2utl.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/pathutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/perfutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/polcutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/precomp.h create mode 100644 src/libs/dutil/WixToolset.DUtil/proc2utl.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/proc3utl.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/procutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/regutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/resrutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/reswutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/rexutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/rmutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/rssutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/sceutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/shelutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/sqlutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/srputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/strutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/svcutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/thmutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/timeutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/uncutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/uriutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/userutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/verutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/wiutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/wuautil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/xmlutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd create mode 100644 src/libs/dutil/appveyor.cmd create mode 100644 src/libs/dutil/appveyor.yml create mode 100644 src/libs/dutil/dutil.sln create mode 100644 src/libs/dutil/nuget.config create mode 100644 src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj create mode 100644 src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters create mode 100644 src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml create mode 100644 src/libs/dutil/test/DUtilUnitTest/UnitTest.rc create mode 100644 src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/error.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/error.h create mode 100644 src/libs/dutil/test/DUtilUnitTest/packages.config create mode 100644 src/libs/dutil/test/DUtilUnitTest/precomp.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/precomp.h create mode 100644 src/libs/dutil/test/DUtilUnitTest/resource.h create mode 100644 src/signing.json delete mode 100644 src/test/DUtilUnitTest/ApupUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/AssemblyInfo.cpp delete mode 100644 src/test/DUtilUnitTest/DUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/DUtilUnitTest.vcxproj delete mode 100644 src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters delete mode 100644 src/test/DUtilUnitTest/DictUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/DirUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/FileUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/GuidUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/IniUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/MemUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/MonUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/PathUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/SceUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/StrUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml delete mode 100644 src/test/DUtilUnitTest/UnitTest.rc delete mode 100644 src/test/DUtilUnitTest/UriUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/VerUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/error.cpp delete mode 100644 src/test/DUtilUnitTest/error.h delete mode 100644 src/test/DUtilUnitTest/packages.config delete mode 100644 src/test/DUtilUnitTest/precomp.cpp delete mode 100644 src/test/DUtilUnitTest/precomp.h delete mode 100644 src/test/DUtilUnitTest/resource.h create mode 100644 src/version.json delete mode 100644 version.json diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1d72e683..00000000 --- a/.editorconfig +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -# -# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig -# then update all of the repos. - -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true - -[*.{cs,vb}] -dotnet_sort_system_directives_first = true - -[*.cs] -csharp_indent_case_contents = true : error -csharp_indent_switch_labels = true : error -csharp_new_line_before_open_brace = all -csharp_prefer_braces = true : error -csharp_style_expression_bodied_methods = when_on_single_line : suggestion -csharp_style_expression_bodied_constructors = when_on_single_line : suggestion -csharp_style_expression_bodied_operators = when_on_single_line : suggestion -csharp_style_expression_bodied_properties = when_on_single_line : suggestion -csharp_style_expression_bodied_indexers = when_on_single_line : suggestion -csharp_style_expression_bodied_accessors = when_on_single_line : suggestion -csharp_style_var_elsewhere = true : suggestion -csharp_style_var_for_built_in_types = true : suggestion -csharp_style_var_when_type_is_apparent = true : suggestion -dotnet_style_qualification_for_event = true : error -dotnet_style_qualification_for_field = true : error -dotnet_style_qualification_for_method = true : error -dotnet_style_qualification_for_property = true : error - -[*.targets] -indent_size = 2 diff --git a/README.md b/README.md deleted file mode 100644 index 2d6605fe..00000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# dutil -dutil.lib - foundation library for all native code in WiX Toolset diff --git a/appveyor.cmd b/appveyor.cmd deleted file mode 100644 index 85476b8e..00000000 --- a/appveyor.cmd +++ /dev/null @@ -1,24 +0,0 @@ -@setlocal -@pushd %~dp0 -@set _C=Release -@if /i "%1"=="debug" set _C=Debug - -nuget restore || exit /b - -msbuild -t:Test -p:Configuration=%_C% src\test\DUtilUnitTest || exit /b - -msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v140 || exit /b -msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v140 || exit /b - -msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v141 || exit /b -msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v141 || exit /b -msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v141 || exit /b - -msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v142 || exit /b -msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v142 || exit /b -msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v142 || exit /b - -msbuild -p:Configuration=%_C% -t:PackNative src\dutil\dutil.vcxproj || exit /b - -@popd -@endlocal \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index f602d07c..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -# -# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml -# then update all of the repos. - -branches: - only: - - master - - develop - -image: Visual Studio 2019 - -version: 0.0.0.{build} -configuration: Release - -environment: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - NUGET_XMLDOC_MODE: skip - -build_script: - - appveyor.cmd - -test: off - -pull_requests: - do_not_increment_build_number: true - -nuget: - disable_publish_on_pr: true - -skip_branch_with_pr: true -skip_tags: true - -artifacts: -- path: build\Release\**\*.nupkg - name: nuget -- path: build\Release\**\*.msi - name: msi - -notifications: -- provider: Slack - incoming_webhook: - secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/dutil.sln b/dutil.sln deleted file mode 100644 index 433f42a5..00000000 --- a/dutil.sln +++ /dev/null @@ -1,47 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30711.63 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DUtilUnitTest", "src\test\DUtilUnitTest\DUtilUnitTest.vcxproj", "{AB7EE608-E5FB-42A5-831F-0DEEEA141223}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.Build.0 = Debug|ARM64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.ActiveCfg = Release|ARM64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.Build.0 = Release|ARM64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32 - {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM64.ActiveCfg = Debug|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x64.ActiveCfg = Debug|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.ActiveCfg = Debug|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.Build.0 = Debug|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM64.ActiveCfg = Release|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x64.ActiveCfg = Release|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.ActiveCfg = Release|Win32 - {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133} - EndGlobalSection -EndGlobal diff --git a/nuget.config b/nuget.config deleted file mode 100644 index d5ef8952..00000000 --- a/nuget.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/signing.json b/signing.json deleted file mode 100644 index fe1c8c9b..00000000 --- a/signing.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "SignClient": { - "AzureAd": { - "AADInstance": "https://login.microsoftonline.com/", - "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", - "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" - }, - "Service": { - "Url": "https://codesign.dotnetfoundation.org/", - "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" - } - } -} diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 00000000..1d72e683 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,37 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig +# then update all of the repos. + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_sort_system_directives_first = true + +[*.cs] +csharp_indent_case_contents = true : error +csharp_indent_switch_labels = true : error +csharp_new_line_before_open_brace = all +csharp_prefer_braces = true : error +csharp_style_expression_bodied_methods = when_on_single_line : suggestion +csharp_style_expression_bodied_constructors = when_on_single_line : suggestion +csharp_style_expression_bodied_operators = when_on_single_line : suggestion +csharp_style_expression_bodied_properties = when_on_single_line : suggestion +csharp_style_expression_bodied_indexers = when_on_single_line : suggestion +csharp_style_expression_bodied_accessors = when_on_single_line : suggestion +csharp_style_var_elsewhere = true : suggestion +csharp_style_var_for_built_in_types = true : suggestion +csharp_style_var_when_type_is_apparent = true : suggestion +dotnet_style_qualification_for_event = true : error +dotnet_style_qualification_for_field = true : error +dotnet_style_qualification_for_method = true : error +dotnet_style_qualification_for_property = true : error + +[*.targets] +indent_size = 2 diff --git a/src/CustomizedNativeRecommendedRules.ruleset b/src/CustomizedNativeRecommendedRules.ruleset deleted file mode 100644 index 142b141c..00000000 --- a/src/CustomizedNativeRecommendedRules.ruleset +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props deleted file mode 100644 index fb34d54e..00000000 --- a/src/Directory.Build.props +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Debug - false - - $(MSBuildProjectName) - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) - $(BaseOutputPath)obj\$(ProjectName)\ - $(BaseOutputPath)$(Configuration)\ - - WiX Toolset Team - WiX Toolset - Copyright (c) .NET Foundation and contributors. All rights reserved. - MS-RL - WiX Toolset - - - - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets deleted file mode 100644 index 44701fb6..00000000 --- a/src/Directory.Build.targets +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - $(BaseOutputPath)obj\.tools - $(SigningToolFolder)\SignClient.exe - $(SigningToolFolder)\empty-filelist.txt - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json - - - - false - $(OutputPath)\$(AssemblyName).xml - - - - - $(PrivateRepositoryUrl.Replace('.git','')) - - $(MSBuildProjectName).nuspec - $(MSBuildProjectDirectory) - $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" - $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) - true - snupkg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Directory.csproj.props b/src/Directory.csproj.props deleted file mode 100644 index 81d24ad1..00000000 --- a/src/Directory.csproj.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - true - true - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) - false - - diff --git a/src/Directory.vcxproj.props b/src/Directory.vcxproj.props deleted file mode 100644 index 9ea7071b..00000000 --- a/src/Directory.vcxproj.props +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - Win32 - $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ - $(OutputPath)$(Platform)\ - - - $(Company) - $(Copyright) - - win-x86;win-x64;win-arm64 - native,Version=v0.0 - - - - $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) - - - - $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset - - - - - $(DisableSpecificCompilerWarnings) - Level4 - $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) - Use - precomp.h - StdCall - true - false - -YlprecompDefine - /Zc:threadSafeInit- %(AdditionalOptions) - true - - - $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) - $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) - - - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) - - - $(ProjectSubSystem) - $(ProjectModuleDefinitionFile) - $(ResourceOnlyDll) - true - $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) - /IGNORE:4099 %(AdditionalOptions) - - - - - - NoExtensions - - - - - CDecl - - - - - OldStyle - true - true - - - - - Disabled - EnableFastChecks - _DEBUG;DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - - MultiThreadedDebugDll - - - - - MinSpace - NDEBUG;%(PreprocessorDefinitions) - true - true - MultiThreaded - - - true - true - - - - - - MultiThreadedDll - - - - - $(LinkKeyFile) - $(LinkDelaySign) - - - diff --git a/src/NativeMultiTargeting.Build.props b/src/NativeMultiTargeting.Build.props deleted file mode 100644 index 1ff46559..00000000 --- a/src/NativeMultiTargeting.Build.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ - $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ - - diff --git a/src/dutil/acl2util.cpp b/src/dutil/acl2util.cpp deleted file mode 100644 index 598f12e7..00000000 --- a/src/dutil/acl2util.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) -#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -AclCalculateServiceSidString - gets the SID string for the given service name - -NOTE: psczSid should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI AclCalculateServiceSidString( - __in LPCWSTR wzServiceName, - __in SIZE_T cchServiceName, - __deref_out_z LPWSTR* psczSid - ) -{ - // TODO: use undocumented RtlCreateServiceSid function? - // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx - // Assume little endian. - HRESULT hr = S_OK; - LPWSTR sczUpperServiceName = NULL; - DWORD cbHash = SHA1_HASH_LEN; - BYTE* pbHash = NULL; - - Assert(psczSid); - - if (0 == cchServiceName) - { - hr = ::StringCchLengthW(wzServiceName, STRSAFE_MAX_CCH, reinterpret_cast(&cchServiceName)); - AclExitOnFailure(hr, "Failed to get the length of the service name."); - } - - hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName); - AclExitOnFailure(hr, "Failed to upper case the service name."); - - pbHash = reinterpret_cast(MemAlloc(cbHash, TRUE)); - AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); - - hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * sizeof(WCHAR), PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); - AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); - - hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u", - MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])), - MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])), - MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])), - MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])), - MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19]))); - -LExit: - ReleaseMem(pbHash); - ReleaseStr(sczUpperServiceName); - - return hr; -} - - -/******************************************************************** -AclGetAccountSidStringEx - gets a string version of the account's SID - calculates a service's SID if lookup fails - -NOTE: psczSid should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI AclGetAccountSidStringEx( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* psczSid - ) -{ - HRESULT hr = S_OK; - SIZE_T cchAccount = 0; - PSID psid = NULL; - LPWSTR pwz = NULL; - LPWSTR sczSid = NULL; - - Assert(psczSid); - - hr = AclGetAccountSid(wzSystem, wzAccount, &psid); - if (SUCCEEDED(hr)) - { - Assert(::IsValidSid(psid)); - - if (!::ConvertSidToStringSidW(psid, &pwz)) - { - AclExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount); - } - - hr = StrAllocString(psczSid, pwz, 0); - } - else - { - if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) - { - HRESULT hrLength = ::StringCchLengthW(wzAccount, STRSAFE_MAX_CCH, reinterpret_cast(&cchAccount)); - AclExitOnFailure(hrLength, "Failed to get the length of the account name."); - - if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11)) - { - // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it. - LPCWSTR wzServiceName = &wzAccount[11]; - hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid); - AclExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName); - - *psczSid = sczSid; - sczSid = NULL; - } - } - AclExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount); - } - -LExit: - ReleaseStr(sczSid); - if (pwz) - { - ::LocalFree(pwz); - } - if (psid) - { - AclFreeSid(psid); - } - - return hr; -} diff --git a/src/dutil/aclutil.cpp b/src/dutil/aclutil.cpp deleted file mode 100644 index c9733033..00000000 --- a/src/dutil/aclutil.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) -#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -AclCheckAccess - determines if token has appropriate privileges - -NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero -if hToken is NULL, the thread will be checked -if hToken is not NULL the token must be an impersonation token -********************************************************************/ -extern "C" HRESULT DAPI AclCheckAccess( - __in HANDLE hToken, - __in ACL_ACCESS* paa - ) -{ - HRESULT hr = S_OK; - PSID psid = NULL; - BOOL fIsMember = FALSE; - - AclExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check"); - Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask); - - if (paa->pwzAccountName) - { - hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid); - AclExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName); - } - else - { - 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)) - { - AclExitWithLastError(hr, "failed to initialize SID"); - } - } - - if (!::CheckTokenMembership(hToken, psid, &fIsMember)) - { - AclExitWithLastError(hr, "failed to check membership"); - } - - fIsMember ? hr = S_OK : hr = S_FALSE; - -LExit: - if (psid) - { - ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid? - } - - return hr; -} - - -/******************************************************************** -AclCheckAdministratorAccess - determines if token has Administrator privileges - -NOTE: if hToken is NULL, the thread will be checked -if hToken is not NULL the token must be an impersonation token -********************************************************************/ -extern "C" HRESULT DAPI AclCheckAdministratorAccess( - __in HANDLE hToken - ) -{ - ACL_ACCESS aa; - SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; - - memset(&aa, 0, sizeof(aa)); - aa.sia = siaNt; - aa.nSubAuthorityCount = 2; - aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID; - aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS; - - return AclCheckAccess(hToken, &aa); -} - - -/******************************************************************** -AclCheckLocalSystemAccess - determines if token has LocalSystem privileges - -NOTE: if hToken is NULL, the thread will be checked -if hToken is not NULL the token must be an impersonation token -********************************************************************/ -extern "C" HRESULT DAPI AclCheckLocalSystemAccess( - __in HANDLE hToken - ) -{ - ACL_ACCESS aa; - SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; - - memset(&aa, 0, sizeof(aa)); - aa.sia = siaNt; - aa.nSubAuthorityCount = 1; - aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID; - - return AclCheckAccess(hToken, &aa); -} - - -/******************************************************************** -AclGetWellKnownSid - returns a SID for the specified account - -********************************************************************/ -extern "C" HRESULT DAPI AclGetWellKnownSid( - __in WELL_KNOWN_SID_TYPE wkst, - __deref_out PSID* ppsid - ) -{ - Assert(ppsid); - - HRESULT hr = S_OK;; - PSID psid = NULL; - DWORD cbSid = SECURITY_MAX_SID_SIZE; - - PSID psidTemp = NULL; -#if(_WIN32_WINNT < 0x0501) - SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; - SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY; - SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY; - BOOL fSuccess = FALSE; -#endif - - // - // allocate memory for the SID and get it - // - psid = static_cast(MemAlloc(cbSid, TRUE)); - AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID"); - -#if(_WIN32_WINNT < 0x0501) - switch (wkst) - { - case WinWorldSid: // Everyone - fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinAuthenticatedUserSid: // Authenticated Users - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinLocalSystemSid: // LocalSystem - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinLocalServiceSid: // LocalService - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinNetworkServiceSid: // NetworkService - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinBuiltinGuestsSid: // Guests - fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinBuiltinAdministratorsSid: // Administrators - fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinBuiltinUsersSid: // Users - fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinCreatorOwnerSid: //CREATOR OWNER - fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinInteractiveSid: // INTERACTIVE - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - default: - hr = E_INVALIDARG; - AclExitOnFailure(hr, "unknown well known SID: %d", wkst); - } - - if (!fSuccess) - AclExitOnLastError(hr, "failed to allocate well known SID: %d", wkst); - - if (!::CopySid(cbSid, psid, psidTemp)) - AclExitOnLastError(hr, "failed to create well known SID: %d", wkst); -#else - Assert(NULL == psidTemp); - if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid)) - { - AclExitWithLastError(hr, "failed to create well known SID: %d", wkst); - } -#endif - - *ppsid = psid; - psid = NULL; // null it here so it won't be released below - - Assert(S_OK == hr && ::IsValidSid(*ppsid)); -LExit: - if (psidTemp) - { - ::FreeSid(psidTemp); - } - - ReleaseMem(psid); - - return hr; -} - - -/******************************************************************** -AclGetAccountSid - returns a SID for the specified account - -********************************************************************/ -extern "C" HRESULT DAPI AclGetAccountSid( - __in_opt LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out PSID* ppsid - ) -{ - Assert(wzAccount && *wzAccount && ppsid); - - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - PSID psid = NULL; - DWORD cbSid = SECURITY_MAX_SID_SIZE; - LPWSTR pwzDomainName = NULL; - DWORD cbDomainName = 255; - SID_NAME_USE peUse; - - // - // allocate memory for the SID and domain name - // - psid = static_cast(MemAlloc(cbSid, TRUE)); - AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID"); - hr = StrAlloc(&pwzDomainName, cbDomainName); - AclExitOnFailure(hr, "failed to allocate string for domain name"); - - // - // try to lookup the account now - // - if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) - { - // if one of the buffers wasn't large enough - er = ::GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == er) - { - if (SECURITY_MAX_SID_SIZE < cbSid) - { - PSID psidNew = static_cast(MemReAlloc(psid, cbSid, TRUE)); - AclExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount); - - psid = psidNew; - } - if (255 < cbDomainName) - { - hr = StrAlloc(&pwzDomainName, cbDomainName); - AclExitOnFailure(hr, "failed to allocate string for domain name"); - } - - if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) - { - AclExitWithLastError(hr, "failed to lookup account: %ls", wzAccount); - } - } - else - { - AclExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount); - } - } - - *ppsid = psid; - psid = NULL; - - hr = S_OK; -LExit: - ReleaseStr(pwzDomainName); - ReleaseMem(psid); - - return hr; -} - - -/******************************************************************** -AclGetAccountSidString - gets a string version of the user's SID - -NOTE: ppwzSid should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI AclGetAccountSidString( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* ppwzSid - ) -{ - Assert(ppwzSid); - HRESULT hr = S_OK; - PSID psid = NULL; - LPWSTR pwz = NULL; - - *ppwzSid = NULL; - - hr = AclGetAccountSid(wzSystem, wzAccount, &psid); - AclExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount); - Assert(::IsValidSid(psid)); - - if (!::ConvertSidToStringSidW(psid, &pwz)) - { - AclExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount); - } - - hr = StrAllocString(ppwzSid, pwz, 0); - -LExit: - if (FAILED(hr)) - { - ReleaseNullStr(*ppwzSid); - } - - if (pwz) - { - ::LocalFree(pwz); - } - - if (psid) - { - AclFreeSid(psid); - } - - return hr; -} - - -/******************************************************************** -AclCreateDacl - creates a DACL from ACL_ACE structures - -********************************************************************/ -extern "C" HRESULT DAPI AclCreateDacl( - __in_ecount(cDeny) ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount(cAllow) ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAcl - ) -{ - Assert(ppAcl); - HRESULT hr = S_OK; - ACL* pAcl = NULL; - DWORD cbAcl = 0; - DWORD i; - - *ppAcl = NULL; - - // initialize the ACL - cbAcl = sizeof(ACL); - for (i = 0; i < cDeny; ++i) - { - cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD); - } - - for (i = 0; i < cAllow; ++i) - { - cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD); - } - - pAcl = static_cast(MemAlloc(cbAcl, TRUE)); - AclExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); - -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to initialize ACL"); - } - - // add in the ACEs (denied first) - for (i = 0; i < cDeny; ++i) - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i); - } - } - for (i = 0; i < cAllow; ++i) - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access allowed ACE #%d to ACL", i); - } - } - - *ppAcl = pAcl; - pAcl = NULL; - AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL"); - Assert(S_OK == hr); -LExit: - if (pAcl) - { - AclFreeDacl(pAcl); - } - - return hr; -} - - -/******************************************************************** -AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure - -********************************************************************/ -extern "C" HRESULT DAPI AclAddToDacl( - __in ACL* pAcl, - __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAclNew - ) -{ - Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew); - HRESULT hr = S_OK; - - ACL_SIZE_INFORMATION asi; - ACL_ACE* paaNewDeny = NULL; - DWORD cNewDeny = 0; - ACL_ACE* paaNewAllow = NULL; - DWORD cNewAllow = 0; - - ACCESS_ALLOWED_ACE* paaa; - ACCESS_DENIED_ACE* pada; - DWORD i; - - // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay) - if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation)) - { - AclExitWithLastError(hr, "failed to get information about original ACL"); - } - - if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow - (asi.AceCount + cDeny) < cDeny || // check for overflow - (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE)) - { - hr = E_OUTOFMEMORY; - AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny)); - } - - paaNewDeny = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE)); - AclExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs"); - - if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow - (asi.AceCount + cAllow) < cAllow || // check for overflow - (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE)) - { - hr = E_OUTOFMEMORY; - AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow)); - } - - paaNewAllow = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE)); - AclExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs"); - - // fill in the new structures with old data then new data (denied first) - for (i = 0; i < asi.AceCount; ++i) - { - if (!::GetAce(pAcl, i, reinterpret_cast(&pada))) - { - AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); - } - - if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType) - { - continue; // skip non-denied aces - } - - paaNewDeny[i].dwFlags = pada->Header.AceFlags; - paaNewDeny[i].dwMask = pada->Mask; - paaNewDeny[i].psid = reinterpret_cast(&(pada->SidStart)); - ++cNewDeny; - } - - memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny); - cNewDeny += cDeny; - - - for (i = 0; i < asi.AceCount; ++i) - { - if (!::GetAce(pAcl, i, reinterpret_cast(&paaa))) - { - AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); - } - - if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType) - { - continue; // skip non-allowed aces - } - - paaNewAllow[i].dwFlags = paaa->Header.AceFlags; - paaNewAllow[i].dwMask = paaa->Mask; - paaNewAllow[i].psid = reinterpret_cast(&(paaa->SidStart)); - ++cNewAllow; - } - - memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow); - cNewAllow += cAllow; - - // create the dacl with the new - hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew); - AclExitOnFailure(hr, "failed to create new ACL from existing ACL"); - - AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL"); - Assert(S_OK == hr); -LExit: - ReleaseMem(paaNewAllow); - ReleaseMem(paaNewDeny); - - return hr; -} - - -/******************************************************************** -AclMergeDacls - creates a new DACL from two existing ACLs - -********************************************************************/ -extern "C" HRESULT DAPI AclMergeDacls( - __in const ACL* pAcl1, - __in const ACL* pAcl2, - __deref_out ACL** ppAclNew - ) -{ - HRESULT hr = E_NOTIMPL; - - Assert(pAcl1 && pAcl2 && ppAclNew); - UNREFERENCED_PARAMETER(pAcl1); - UNREFERENCED_PARAMETER(pAcl2); - UNREFERENCED_PARAMETER(ppAclNew); - -//LExit: - return hr; -} - - -/******************************************************************** -AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure - -********************************************************************/ -extern "C" HRESULT DAPI AclCreateDaclOld( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out ACL** ppACL - ) -{ - Assert(ppACL); - HRESULT hr = S_OK; - DWORD* pdwAccessMask = NULL; - PSID* ppsid = NULL; - - DWORD i; - int cbAcl; - - *ppACL = NULL; - - // - // create the SIDs and calculate the space for the ACL - // - pdwAccessMask = static_cast(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE)); - AclExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask"); - ppsid = static_cast(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE)); - AclExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid"); - - cbAcl = sizeof (ACL); // start with the size of the header - for (i = 0; i < cAclAccesses; ++i) - { - if (paa[i].pwzAccountName) - { - hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i); - AclExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName); - } - else - { - if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount, - paa[i].nSubAuthority[0], paa[i].nSubAuthority[1], - paa[i].nSubAuthority[2], paa[i].nSubAuthority[3], - paa[i].nSubAuthority[4], paa[i].nSubAuthority[5], - paa[i].nSubAuthority[6], paa[i].nSubAuthority[7], - (void**)(ppsid + i)))) - { - AclExitWithLastError(hr, "failed to initialize SIDs #%u", i); - } - } - - // add the newly allocated SID size to the count of bytes for this ACL - cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD); - if (paa[i].fDenyAccess) - { - cbAcl += sizeof(ACCESS_DENIED_ACE); - } - else - { - cbAcl += sizeof(ACCESS_ALLOWED_ACE); - } - - pdwAccessMask[i] = paa[i].dwAccessMask; - } - - // - // allocate the ACL and set the appropriate ACEs - // - *ppACL = static_cast(MemAlloc(cbAcl, FALSE)); - AclExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL"); - -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to initialize ACLs"); - } - - // add an access-allowed ACE for each of the SIDs - for (i = 0; i < cAclAccesses; ++i) - { - if (paa[i].fDenyAccess) - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access denied for ACE"); - } - } - else - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access allowed for ACE"); - } - } - } - -LExit: - if (FAILED(hr)) - { - ReleaseNullMem(*ppACL); - } - - if (ppsid) - { - for (i = 0; i < cAclAccesses; ++i) - { - if (ppsid[i]) - { - ::FreeSid(ppsid[i]); - } - } - - MemFree(ppsid); - } - - ReleaseMem(pdwAccessMask); - - return hr; -} - - -/******************************************************************** -AclCreateSecurityDescriptorFromDacl - creates a self-relative security -descriptor from an existing DACL - -********************************************************************/ -extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( - __in ACL* pACL, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - HRESULT hr = S_OK; - - SECURITY_DESCRIPTOR sd; - DWORD cbSD; - - AclExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided"); - AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided"); - - *ppsd = NULL; - - // - // create the absolute security descriptor - // - - // initialize our security descriptor, throw the ACL into it, and set the owner -#pragma prefast(push) -#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs -#pragma prefast(disable:25029) - if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) || - (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) || - (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE))) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to initialize security descriptor"); - } - - // - // create the self-relative security descriptor - // - cbSD = ::GetSecurityDescriptorLength(&sd); - *ppsd = static_cast(MemAlloc(cbSD, FALSE)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); - - ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - return hr; -} - - -/******************************************************************** -AclCreateSecurityDescriptor - creates a self-relative security descriptor from an -ACL_ACCESS structure - -NOTE: ppsd should be freed with AclFreeSecurityDescriptor() -********************************************************************/ -extern "C" HRESULT DAPI AclCreateSecurityDescriptor( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - Assert(ppsd); - HRESULT hr = S_OK; - - ACL* pACL; - - *ppsd = NULL; - - // - // create the DACL - // - hr = AclCreateDaclOld(paa, cAclAccesses, &pACL); - AclExitOnFailure(hr, "failed to create DACL for security descriptor"); - - // - // create self-relative security descriptor - // - hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd); - -LExit: - return hr; -} - - -/******************************************************************** -AclCreateSecurityDescriptorFromString - creates a self-relative security -descriptor from an SDDL string - -NOTE: ppsd should be freed with AclFreeSecurityDescriptor() -********************************************************************/ -extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString( - __deref_out SECURITY_DESCRIPTOR** ppsd, - __in_z __format_string LPCWSTR wzSddlFormat, - ... - ) -{ - Assert(ppsd); - HRESULT hr = S_OK; - LPWSTR pwzSddl = NULL; - va_list args; - PSECURITY_DESCRIPTOR psd = NULL; - DWORD cbSD = 0; - - *ppsd = NULL; - - va_start(args, wzSddlFormat); - hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args); - va_end(args); - AclExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat); - - if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD)) - { - AclExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl); - } - - *ppsd = static_cast(MemAlloc(cbSD, FALSE)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor"); - - memcpy(*ppsd, psd, cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - - Assert(S_OK == hr); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - if (psd) - { - ::LocalFree(psd); - } - - ReleaseStr(pwzSddl); - return hr; -} - - -/******************************************************************** -AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor - -NOTE: passed in security descriptor must be in self-relative format -********************************************************************/ -extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - HRESULT hr = S_OK; - DWORD cbSD; - - AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided"); - *ppsd = NULL; - - // - // create the self-relative security descriptor - // - cbSD = ::GetSecurityDescriptorLength(psd); - *ppsd = static_cast(MemAlloc(cbSD, 0)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); - - memcpy(*ppsd, psd, cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - return hr; -} - - -/******************************************************************** -AclGetSecurityDescriptor - returns self-relative security descriptor for named object - -NOTE: free ppsd with AclFreeSecurityDescriptor() -********************************************************************/ -extern "C" HRESULT DAPI AclGetSecurityDescriptor( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - HRESULT hr = S_OK; - DWORD er; - PSECURITY_DESCRIPTOR psd = NULL; - DWORD cbSD; - - AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided"); - *ppsd = NULL; - - // get the security descriptor for the object - er = ::GetNamedSecurityInfoW(const_cast(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd); - AclExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject); - Assert(::IsValidSecurityDescriptor(psd)); - - // copy the self-relative security descriptor - cbSD = ::GetSecurityDescriptorLength(psd); - *ppsd = static_cast(MemAlloc(cbSD, 0)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); - - memcpy(*ppsd, psd, cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - if (psd) - { - ::LocalFree(psd); - } - - return hr; -} - - -extern "C" HRESULT DAPI AclSetSecurityWithRetry( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __in_opt PSID psidOwner, - __in_opt PSID psidGroup, - __in_opt PACL pDacl, - __in_opt PACL pSacl, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ) -{ - HRESULT hr = S_OK; - LPWSTR sczObject = NULL; - DWORD i = 0; - - hr = StrAllocString(&sczObject, wzObject, 0); - AclExitOnFailure(hr, "Failed to copy object to secure."); - - hr = E_FAIL; - for (i = 0; FAILED(hr) && i <= cRetry; ++i) - { - if (0 < i) - { - ::Sleep(dwWaitMilliseconds); - } - - DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl); - hr = HRESULT_FROM_WIN32(er); - } - AclExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i); - -LExit: - ReleaseStr(sczObject); - - return hr; -} - - -/******************************************************************** -AclFreeSid - frees a SID created by any Acl* functions - -********************************************************************/ -extern "C" HRESULT DAPI AclFreeSid( - __in PSID psid - ) -{ - Assert(psid && ::IsValidSid(psid)); - HRESULT hr = S_OK; - - hr = MemFree(psid); - - return hr; -} - - -/******************************************************************** -AclFreeDacl - frees a DACL created by any Acl* functions - -********************************************************************/ -extern "C" HRESULT DAPI AclFreeDacl( - __in ACL* pACL - ) -{ - Assert(pACL); - HRESULT hr = S_OK; - - hr = MemFree(pACL); - - return hr; -} - - -/******************************************************************** -AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions - -********************************************************************/ -extern "C" HRESULT DAPI AclFreeSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd - ) -{ - Assert(psd && ::IsValidSecurityDescriptor(psd)); - HRESULT hr = S_OK; - - hr = MemFree(psd); - - return hr; -} - - -/******************************************************************** -AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor - -********************************************************************/ -extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor( - __in SECURITY_DESCRIPTOR* pSecurity, - __deref_out SECURITY_DESCRIPTOR** ppSecurityNew - ) -{ - HRESULT hr = S_OK; - PACL pAcl = NULL; - PACL pAclNew = NULL; - BOOL fValid, fDaclDefaulted; - ACL_ACE ace[1]; - SECURITY_DESCRIPTOR* pSecurityNew; - - if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid) - { - AclExitOnLastError(hr, "Failed to get acl from security descriptor"); - } - - hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid); - AclExitOnFailure(hr, "failed to get sid for Administrators group"); - - ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE; - ace[0].dwMask = GENERIC_ALL; - - hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew); - AclExitOnFailure(hr, "failed to add Administrators ACE to ACL"); - - hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew); - AclExitOnLastError(hr, "Failed to create new security descriptor"); - - // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it. - pAclNew = NULL; - - *ppSecurityNew = pSecurityNew; - -LExit: - if (pAclNew) - { - AclFreeDacl(pAclNew); - } - if (ace[0].psid) - { - AclFreeSid(ace[0].psid); - } - - return hr; -} diff --git a/src/dutil/apputil.cpp b/src/dutil/apputil.cpp deleted file mode 100644 index 589a09dd..00000000 --- a/src/dutil/apputil.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AppExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) -#define AppExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) -#define AppExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) -#define AppExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) -#define AppExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APPUTIL, e, x, s, __VA_ARGS__) -#define AppExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APPUTIL, g, x, s, __VA_ARGS__) - -const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; -typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); -typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); - -extern "C" void DAPI AppFreeCommandLineArgs( - __in LPWSTR* argv - ) -{ - // The "ignored" hack in AppParseCommandLine requires an adjustment. - LPWSTR* argvOriginal = argv - 1; - ::LocalFree(argvOriginal); -} - -/******************************************************************** -AppInitialize - initializes the standard safety precautions for an - installation application. - -********************************************************************/ -extern "C" void DAPI AppInitialize( - __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], - __in DWORD cSafelyLoadSystemDlls - ) -{ - HRESULT hr = S_OK; - HMODULE hIgnored = NULL; - BOOL fSetDefaultDllDirectories = FALSE; - - ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); - - // Best effort call to initialize default DLL directories to system only. - HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32"); - Assert(hKernel32); - LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories"); - if (pfnSetDefaultDllDirectories) - { - if (pfnSetDefaultDllDirectories(PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32)) - { - fSetDefaultDllDirectories = TRUE; - } - else - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to call SetDefaultDllDirectories."); - } - } - - // Only need to safely load if the default DLL directories was not - // able to be set. - if (!fSetDefaultDllDirectories) - { - // Remove current working directory from search order. - LPFN_SETDLLDIRECTORYW pfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW"); - if (!pfnSetDllDirectory || !pfnSetDllDirectory(L"")) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to call SetDllDirectory."); - } - - for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i) - { - hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored); - if (FAILED(hr)) - { - TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]); - } - } - } -} - -extern "C" void DAPI AppInitializeUnsafe() -{ - ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); -} - -extern "C" DAPI_(HRESULT) AppParseCommandLine( - __in LPCWSTR wzCommandLine, - __in int* pArgc, - __in LPWSTR** pArgv - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCommandLine = NULL; - LPWSTR* argv = NULL; - int argc = 0; - - // CommandLineToArgvW tries to treat the first argument as the path to the process, - // which fails pretty miserably if your first argument is something like - // FOO="C:\Program Files\My Company". So give it something harmless to play with. - hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0); - AppExitOnFailure(hr, "Failed to initialize command line."); - - hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0); - AppExitOnFailure(hr, "Failed to copy command line."); - - argv = ::CommandLineToArgvW(sczCommandLine, &argc); - AppExitOnNullWithLastError(argv, hr, "Failed to parse command line."); - - // Skip "ignored" argument/hack. - *pArgv = argv + 1; - *pArgc = argc - 1; - -LExit: - ReleaseStr(sczCommandLine); - - return hr; -} diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp deleted file mode 100644 index eb96d515..00000000 --- a/src/dutil/apuputil.cpp +++ /dev/null @@ -1,700 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define ApupExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) -#define ApupExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) -#define ApupExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) -#define ApupExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) -#define ApupExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APUPUTIL, e, x, s, __VA_ARGS__) -#define ApupExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APUPUTIL, g, x, s, __VA_ARGS__) - -// prototypes -static HRESULT ProcessEntry( - __in ATOM_ENTRY* pAtomEntry, - __in LPCWSTR wzDefaultAppId, - __inout APPLICATION_UPDATE_ENTRY* pApupEntry - ); -static HRESULT ParseEnclosure( - __in ATOM_LINK* pLink, - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ); -static __callback int __cdecl CompareEntries( - void* pvContext, - const void* pvLeft, - const void* pvRight - ); -static HRESULT FilterEntries( - __in APPLICATION_UPDATE_ENTRY* rgEntries, - __in DWORD cEntries, - __in VERUTIL_VERSION* pCurrentVersion, - __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, - __inout DWORD* pcFilteredEntries - ); -static HRESULT CopyEntry( - __in const APPLICATION_UPDATE_ENTRY* pSrc, - __in APPLICATION_UPDATE_ENTRY* pDest - ); -static HRESULT CopyEnclosure( - __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, - __in APPLICATION_UPDATE_ENCLOSURE* pDest - ); -static void FreeEntry( - __in APPLICATION_UPDATE_ENTRY* pApupEntry - ); -static void FreeEnclosure( - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ); - - -// -// ApupCalculateChainFromAtom - returns the chain of application updates found in an ATOM feed. -// -extern "C" HRESULT DAPI ApupAllocChainFromAtom( - __in ATOM_FEED* pFeed, - __out APPLICATION_UPDATE_CHAIN** ppChain - ) -{ - HRESULT hr = S_OK; - APPLICATION_UPDATE_CHAIN* pChain = NULL; - - pChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); - - // First search the ATOM feed's custom elements to try and find the default application identity. - for (ATOM_UNKNOWN_ELEMENT* pElement = pFeed->pUnknownElements; pElement; pElement = pElement->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) - { - hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate default application id."); - - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) - { - hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate default application type."); - } - } - } - } - } - - // Assume there will be as many application updates entries as their are feed entries. - if (pFeed->cEntries) - { - pChain->rgEntries = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE)); - ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries."); - - // Process each entry, building up the chain. - for (DWORD i = 0; i < pFeed->cEntries; ++i) - { - hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries); - ApupExitOnFailure(hr, "Failed to process ATOM entry."); - - if (S_FALSE != hr) - { - ++pChain->cEntries; - } - } - - // Sort the chain by descending version and ascending total size. - qsort_s(pChain->rgEntries, pChain->cEntries, sizeof(APPLICATION_UPDATE_ENTRY), CompareEntries, NULL); - } - - // Trim the unused entries from the end, if any of the entries failed to parse or validate - if (pChain->cEntries != pFeed->cEntries) - { - if (pChain->cEntries > 0) - { - pChain->rgEntries = static_cast(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE)); - ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries."); - } - else - { - ReleaseNullMem(pChain->rgEntries); - } - } - - *ppChain = pChain; - pChain = NULL; - -LExit: - ReleaseApupChain(pChain); - - return hr; -} - - -// -// ApupFilterChain - remove the unneeded update elements from the chain. -// -HRESULT DAPI ApupFilterChain( - __in APPLICATION_UPDATE_CHAIN* pChain, - __in VERUTIL_VERSION* pVersion, - __out APPLICATION_UPDATE_CHAIN** ppFilteredChain - ) -{ - HRESULT hr = S_OK; - APPLICATION_UPDATE_CHAIN* pNewChain = NULL; - APPLICATION_UPDATE_ENTRY* prgEntries = NULL; - DWORD cEntries = NULL; - - pNewChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); - ApupExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); - - hr = FilterEntries(pChain->rgEntries, pChain->cEntries, pVersion, &prgEntries, &cEntries); - ApupExitOnFailure(hr, "Failed to filter entries by version."); - - if (pChain->wzDefaultApplicationId) - { - hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0); - ApupExitOnFailure(hr, "Failed to copy default application id."); - } - - if (pChain->wzDefaultApplicationType) - { - hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0); - ApupExitOnFailure(hr, "Failed to copy default application type."); - } - - pNewChain->rgEntries = prgEntries; - pNewChain->cEntries = cEntries; - - *ppFilteredChain = pNewChain; - pNewChain = NULL; - -LExit: - ReleaseApupChain(pNewChain); - return hr; -} - - -// -// ApupFreeChain - frees a previously allocated application update chain. -// -extern "C" void DAPI ApupFreeChain( - __in APPLICATION_UPDATE_CHAIN* pChain - ) -{ - if (pChain) - { - for (DWORD i = 0; i < pChain->cEntries; ++i) - { - FreeEntry(pChain->rgEntries + i); - } - - ReleaseMem(pChain->rgEntries); - ReleaseStr(pChain->wzDefaultApplicationType); - ReleaseStr(pChain->wzDefaultApplicationId); - ReleaseMem(pChain); - } -} - - -static HRESULT ProcessEntry( - __in ATOM_ENTRY* pAtomEntry, - __in LPCWSTR wzDefaultAppId, - __inout APPLICATION_UPDATE_ENTRY* pApupEntry - ) -{ - HRESULT hr = S_OK; - int nCompareResult = 0; - - // First search the ATOM entry's custom elements to try and find the application update information. - for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) - { - hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate application identity."); - - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) - { - hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate application type."); - } - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1)) - { - hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate upgrade id."); - - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1)) - { - hr = VerParseVersion(pAttribute->wzValue, 0, FALSE, &pApupEntry->pUpgradeVersion); - ApupExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzValue, -1, L"true", -1)) - { - pApupEntry->fUpgradeExclusive = TRUE; - } - } - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1)) - { - hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion); - ApupExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); - } - } - } - - // If there is no application identity or no version, skip the whole thing. - if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !pApupEntry->pVersion) - { - ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version. - } - - if (pApupEntry->pUpgradeVersion) - { - hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare version to upgrade version."); - - if (nCompareResult >= 0) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); - } - } - - if (pAtomEntry->wzTitle) - { - hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0); - ApupExitOnFailure(hr, "Failed to allocate application title."); - } - - if (pAtomEntry->wzSummary) - { - hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0); - ApupExitOnFailure(hr, "Failed to allocate application summary."); - } - - if (pAtomEntry->pContent) - { - if (pAtomEntry->pContent->wzType) - { - hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0); - ApupExitOnFailure(hr, "Failed to allocate content type."); - } - - if (pAtomEntry->pContent->wzValue) - { - hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate content."); - } - } - // Now process the enclosures. Assume every link in the ATOM entry is an enclosure. - pApupEntry->rgEnclosures = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE)); - ApupExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry."); - - for (DWORD i = 0; i < pAtomEntry->cLinks; ++i) - { - ATOM_LINK* pLink = pAtomEntry->rgLinks + i; - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1)) - { - hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures); - ApupExitOnFailure(hr, "Failed to parse enclosure."); - - pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures - - ++pApupEntry->cEnclosures; - } - } - -LExit: - if (S_OK != hr) // if anything went wrong, free the entry. - { - FreeEntry(pApupEntry); - memset(pApupEntry, 0, sizeof(APPLICATION_UPDATE_ENTRY)); - } - - return hr; -} - - -static HRESULT ParseEnclosure( - __in ATOM_LINK* pLink, - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ) -{ - HRESULT hr = S_OK; - DWORD dwDigestLength = 0; - DWORD dwDigestStringLength = 0; - size_t cchDigestString = 0; - - // First search the ATOM link's custom elements to try and find the application update enclosure information. - for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1)) - { - // Find the digest[@algorithm] which is required. Everything else is ignored. - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - dwDigestLength = 0; - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5; - dwDigestLength = MD5_HASH_LEN; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1; - dwDigestLength = SHA1_HASH_LEN; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256; - dwDigestLength = SHA256_HASH_LEN; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha512", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA512; - dwDigestLength = SHA512_HASH_LEN; - } - break; - } - } - - if (dwDigestLength) - { - dwDigestStringLength = 2 * dwDigestLength; - - hr = ::StringCchLengthW(pElement->wzValue, STRSAFE_MAX_CCH, &cchDigestString); - ApupExitOnFailure(hr, "Failed to get string length of digest value."); - - if (dwDigestStringLength != cchDigestString) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Invalid digest length (%Iu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); - } - - pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength; - pEnclosure->rgbDigest = static_cast(MemAlloc(pEnclosure->cbDigest, TRUE)); - ApupExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); - - hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest); - ApupExitOnFailure(hr, "Failed to decode digest value."); - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Unknown algorithm type for digest."); - } - - break; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1)) - { - hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to copy local name."); - } - } - } - - pEnclosure->dw64Size = pLink->dw64Length; - - hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0); - ApupExitOnFailure(hr, "Failed to allocate enclosure URL."); - - pEnclosure->fInstaller = FALSE; - pEnclosure->wzLocalName = NULL; - -LExit: - return hr; -} - - -static __callback int __cdecl CompareEntries( - void* /*pvContext*/, - const void* pvLeft, - const void* pvRight - ) -{ - int ret = 0; - const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast(pvLeft); - const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast(pvRight); - - VerCompareParsedVersions(pEntryLeft->pVersion, pEntryRight->pVersion, &ret); - if (0 == ret) - { - VerCompareParsedVersions(pEntryLeft->pUpgradeVersion, pEntryRight->pUpgradeVersion, &ret); - if (0 == ret) - { - ret = (pEntryLeft->dw64TotalSize < pEntryRight->dw64TotalSize) ? -1 : - (pEntryLeft->dw64TotalSize > pEntryRight->dw64TotalSize) ? 1 : 0; - } - } - - // Sort descending. - ret = -ret; - - return ret; -} - - -static HRESULT FilterEntries( - __in APPLICATION_UPDATE_ENTRY* rgEntries, - __in DWORD cEntries, - __in VERUTIL_VERSION* pCurrentVersion, - __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, - __inout DWORD* pcFilteredEntries - ) -{ - HRESULT hr = S_OK; - int nCompareResult = 0; - size_t cbAllocSize = 0; - const APPLICATION_UPDATE_ENTRY* pRequired = NULL;; - LPVOID pv = NULL; - - if (cEntries) - { - for (DWORD i = 0; i < cEntries; ++i) - { - const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i; - - hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare versions."); - - if (nCompareResult >= 0) - { - continue; - } - - hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pUpgradeVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare upgrade versions."); - - if (nCompareResult > 0 || (!pEntry->fUpgradeExclusive && nCompareResult == 0)) - { - pRequired = pEntry; - break; - } - } - - if (pRequired) - { - DWORD cNewFilteredEntries = *pcFilteredEntries + 1; - - hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize); - ApupExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries); - - if (*prgFilteredEntries) - { - pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE); - ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries."); - } - else - { - pv = MemAlloc(cbAllocSize, TRUE); - ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries."); - } - - *pcFilteredEntries = cNewFilteredEntries; - *prgFilteredEntries = static_cast(pv); - pv = NULL; - - hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1); - ApupExitOnFailure(hr, "Failed to deep copy entry."); - - hr = VerCompareParsedVersions(pRequired->pVersion, rgEntries[0].pVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare required version."); - - if (nCompareResult < 0) - { - FilterEntries(rgEntries, cEntries, pRequired->pVersion, prgFilteredEntries, pcFilteredEntries); - } - } - } - -LExit: - ReleaseMem(pv); - return hr; -} - - -static HRESULT CopyEntry( - __in const APPLICATION_UPDATE_ENTRY* pSrc, - __in APPLICATION_UPDATE_ENTRY* pDest - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - - memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENTRY)); - - if (pSrc->wzApplicationId) - { - hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0); - ApupExitOnFailure(hr, "Failed to copy application id."); - } - - if (pSrc->wzApplicationType) - { - hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0); - ApupExitOnFailure(hr, "Failed to copy application type."); - } - - if (pSrc->wzUpgradeId) - { - hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0); - ApupExitOnFailure(hr, "Failed to copy upgrade id."); - } - - if (pSrc->wzTitle) - { - hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0); - ApupExitOnFailure(hr, "Failed to copy title."); - } - - if (pSrc->wzSummary) - { - hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0); - ApupExitOnFailure(hr, "Failed to copy summary."); - } - - if (pSrc->wzContentType) - { - hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0); - ApupExitOnFailure(hr, "Failed to copy content type."); - } - - if (pSrc->wzContent) - { - hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0); - ApupExitOnFailure(hr, "Failed to copy content."); - } - - pDest->dw64TotalSize = pSrc->dw64TotalSize; - - hr = VerCopyVersion(pSrc->pUpgradeVersion, &pDest->pUpgradeVersion); - ApupExitOnFailure(hr, "Failed to copy upgrade version."); - - hr = VerCopyVersion(pSrc->pVersion, &pDest->pVersion); - ApupExitOnFailure(hr, "Failed to copy version."); - - pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive; - - hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize); - ApupExitOnRootFailure(hr, "Overflow while calculating memory allocation size"); - - pDest->rgEnclosures = static_cast(MemAlloc(cbAllocSize, TRUE)); - ApupExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures."); - - pDest->cEnclosures = pSrc->cEnclosures; - - for (DWORD i = 0; i < pDest->cEnclosures; ++i) - { - hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i); - ApupExitOnFailure(hr, "Failed to copy enclosure."); - } - -LExit: - if (FAILED(hr)) - { - FreeEntry(pDest); - } - - return hr; -} - - -static HRESULT CopyEnclosure( - __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, - __in APPLICATION_UPDATE_ENCLOSURE* pDest - ) -{ - HRESULT hr = S_OK; - - memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENCLOSURE)); - - if (pSrc->wzUrl) - { - hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0); - ApupExitOnFailure(hr, "Failed copy url."); - } - - if (pSrc->wzLocalName) - { - hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0); - ApupExitOnFailure(hr, "Failed copy url."); - } - - pDest->rgbDigest = static_cast(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE)); - ApupExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest."); - - pDest->cbDigest = pSrc->cbDigest; - - memcpy_s(pDest->rgbDigest, sizeof(BYTE) * pDest->cbDigest, pSrc->rgbDigest, sizeof(BYTE) * pSrc->cbDigest); - - pDest->digestAlgorithm = pSrc->digestAlgorithm; - - pDest->dw64Size = pSrc->dw64Size; - pDest->fInstaller = pSrc->fInstaller; - -LExit: - if (FAILED(hr)) - { - FreeEnclosure(pDest); - } - - return hr; -} - - -static void FreeEntry( - __in APPLICATION_UPDATE_ENTRY* pEntry - ) -{ - if (pEntry) - { - for (DWORD i = 0; i < pEntry->cEnclosures; ++i) - { - FreeEnclosure(pEntry->rgEnclosures + i); - } - - ReleaseStr(pEntry->wzUpgradeId); - ReleaseStr(pEntry->wzApplicationType); - ReleaseStr(pEntry->wzApplicationId); - ReleaseStr(pEntry->wzTitle); - ReleaseStr(pEntry->wzSummary); - ReleaseStr(pEntry->wzContentType); - ReleaseStr(pEntry->wzContent); - ReleaseVerutilVersion(pEntry->pVersion); - ReleaseVerutilVersion(pEntry->pUpgradeVersion); - } -} - - -static void FreeEnclosure( - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ) -{ - if (pEnclosure) - { - ReleaseMem(pEnclosure->rgbDigest); - ReleaseStr(pEnclosure->wzLocalName); - ReleaseStr(pEnclosure->wzUrl); - } -} diff --git a/src/dutil/atomutil.cpp b/src/dutil/atomutil.cpp deleted file mode 100644 index c7c7975a..00000000 --- a/src/dutil/atomutil.cpp +++ /dev/null @@ -1,1297 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AtomExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) -#define AtomExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) -#define AtomExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) -#define AtomExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) -#define AtomExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ATOMUTIL, e, x, s, __VA_ARGS__) -#define AtomExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ATOMUTIL, g, x, s, __VA_ARGS__) - -static HRESULT ParseAtomDocument( - __in IXMLDOMDocument *pixd, - __out ATOM_FEED **ppFeed - ); -static HRESULT ParseAtomFeed( - __in IXMLDOMNode *pixnFeed, - __out ATOM_FEED **ppFeed - ); -static HRESULT ParseAtomAuthor( - __in IXMLDOMNode* pixnAuthor, - __in ATOM_AUTHOR* pAuthor - ); -static HRESULT ParseAtomCategory( - __in IXMLDOMNode* pixnCategory, - __in ATOM_CATEGORY* pCategory - ); -static HRESULT ParseAtomEntry( - __in IXMLDOMNode* pixnEntry, - __in ATOM_ENTRY* pEntry - ); -static HRESULT ParseAtomLink( - __in IXMLDOMNode* pixnLink, - __in ATOM_LINK* pLink - ); -static HRESULT ParseAtomUnknownElement( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement - ); -static HRESULT ParseAtomUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ); -static HRESULT AssignDateTime( - __in FILETIME* pft, - __in IXMLDOMNode* pNode - ); -static HRESULT AssignString( - __out_z LPWSTR* pwzValue, - __in IXMLDOMNode* pNode - ); -static void FreeAtomAuthor( - __in_opt ATOM_AUTHOR* pAuthor - ); -static void FreeAtomContent( - __in_opt ATOM_CONTENT* pContent - ); -static void FreeAtomCategory( - __in_opt ATOM_CATEGORY* pCategory - ); -static void FreeAtomEntry( - __in_opt ATOM_ENTRY* pEntry - ); -static void FreeAtomLink( - __in_opt ATOM_LINK* pLink - ); -static void FreeAtomUnknownElementList( - __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement - ); -static void FreeAtomUnknownAttributeList( - __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ); - -template static HRESULT AllocateAtomType( - __in IXMLDOMNode* pixnParent, - __in LPCWSTR wzT, - __out T** pprgT, - __out DWORD* pcT - ); - - -/******************************************************************** - AtomInitialize - Initialize ATOM utilities. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomInitialize() -{ - return XmlInitialize(); -} - - -/******************************************************************** - AtomUninitialize - Uninitialize ATOM utilities. - -*********************************************************************/ -extern "C" void DAPI AtomUninitialize() -{ - XmlUninitialize(); -} - - -/******************************************************************** - AtomParseFromString - parses out an ATOM feed from a string. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomParseFromString( - __in_z LPCWSTR wzAtomString, - __out ATOM_FEED **ppFeed - ) -{ - Assert(wzAtomString); - Assert(ppFeed); - - HRESULT hr = S_OK; - ATOM_FEED *pNewFeed = NULL; - IXMLDOMDocument *pixdAtom = NULL; - - hr = XmlLoadDocument(wzAtomString, &pixdAtom); - AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); - - hr = ParseAtomDocument(pixdAtom, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM document."); - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseAtomFeed(pNewFeed); - ReleaseObject(pixdAtom); - - return hr; -} - - -/******************************************************************** - AtomParseFromFile - parses out an ATOM feed from a file path. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomParseFromFile( - __in_z LPCWSTR wzAtomFile, - __out ATOM_FEED **ppFeed - ) -{ - Assert(wzAtomFile); - Assert(ppFeed); - - HRESULT hr = S_OK; - ATOM_FEED *pNewFeed = NULL; - IXMLDOMDocument *pixdAtom = NULL; - - hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom); - AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); - - hr = ParseAtomDocument(pixdAtom, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM document."); - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseAtomFeed(pNewFeed); - ReleaseObject(pixdAtom); - - return hr; -} - - -/******************************************************************** - AtomParseFromDocument - parses out an ATOM feed from an XML document. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomParseFromDocument( - __in IXMLDOMDocument* pixdDocument, - __out ATOM_FEED **ppFeed - ) -{ - Assert(pixdDocument); - Assert(ppFeed); - - HRESULT hr = S_OK; - ATOM_FEED *pNewFeed = NULL; - - hr = ParseAtomDocument(pixdDocument, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM document."); - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseAtomFeed(pNewFeed); - - return hr; -} - - -/******************************************************************** - AtomFreeFeed - parses out an ATOM feed from a string. - -*********************************************************************/ -extern "C" void DAPI AtomFreeFeed( - __in_xcount(pFeed->cItems) ATOM_FEED* pFeed - ) -{ - if (pFeed) - { - FreeAtomUnknownElementList(pFeed->pUnknownElements); - ReleaseObject(pFeed->pixn); - - for (DWORD i = 0; i < pFeed->cLinks; ++i) - { - FreeAtomLink(pFeed->rgLinks + i); - } - ReleaseMem(pFeed->rgLinks); - - for (DWORD i = 0; i < pFeed->cEntries; ++i) - { - FreeAtomEntry(pFeed->rgEntries + i); - } - ReleaseMem(pFeed->rgEntries); - - for (DWORD i = 0; i < pFeed->cCategories; ++i) - { - FreeAtomCategory(pFeed->rgCategories + i); - } - ReleaseMem(pFeed->rgCategories); - - for (DWORD i = 0; i < pFeed->cAuthors; ++i) - { - FreeAtomAuthor(pFeed->rgAuthors + i); - } - ReleaseMem(pFeed->rgAuthors); - - ReleaseStr(pFeed->wzGenerator); - ReleaseStr(pFeed->wzIcon); - ReleaseStr(pFeed->wzId); - ReleaseStr(pFeed->wzLogo); - ReleaseStr(pFeed->wzSubtitle); - ReleaseStr(pFeed->wzTitle); - - MemFree(pFeed); - } -} - - -/******************************************************************** - ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document. - -*********************************************************************/ -static HRESULT ParseAtomDocument( - __in IXMLDOMDocument *pixd, - __out ATOM_FEED **ppFeed - ) -{ - Assert(pixd); - Assert(ppFeed); - - HRESULT hr = S_OK; - IXMLDOMElement *pFeedElement = NULL; - - ATOM_FEED *pNewFeed = NULL; - - // - // Get the document element and start processing feeds. - // - hr = pixd->get_documentElement(&pFeedElement); - AtomExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); - - hr = ParseAtomFeed(pFeedElement, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM feed."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseObject(pFeedElement); - - ReleaseAtomFeed(pNewFeed); - - return hr; -} - - -/******************************************************************** - ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element. - -*********************************************************************/ -static HRESULT ParseAtomFeed( - __in IXMLDOMNode *pixnFeed, - __out ATOM_FEED **ppFeed - ) -{ - Assert(pixnFeed); - Assert(ppFeed); - - HRESULT hr = S_OK; - IXMLDOMNodeList *pNodeList = NULL; - - ATOM_FEED *pNewFeed = NULL; - DWORD cAuthors = 0; - DWORD cCategories = 0; - DWORD cEntries = 0; - DWORD cLinks = 0; - - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // First, allocate the new feed and all the possible sub elements. - pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE); - AtomExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); - - pNewFeed->pixn = pixnFeed; - pNewFeed->pixn->AddRef(); - - hr = AllocateAtomType(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed authors."); - - hr = AllocateAtomType(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed categories."); - - hr = AllocateAtomType(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed entries."); - - hr = AllocateAtomType(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed links."); - - // Second, process the elements under a feed. - hr = pixnFeed->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1)) - { - hr = AssignString(&pNewFeed->wzGenerator, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed generator."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1)) - { - hr = AssignString(&pNewFeed->wzIcon, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed icon."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) - { - hr = AssignString(&pNewFeed->wzId, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed id."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1)) - { - hr = AssignString(&pNewFeed->wzLogo, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed logo."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1)) - { - hr = AssignString(&pNewFeed->wzSubtitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) - { - hr = AssignString(&pNewFeed->wzTitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed title."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) - { - hr = AssignDateTime(&pNewFeed->ftUpdated, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed updated."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) - { - hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]); - AtomExitOnFailure(hr, "Failed to parse ATOM author."); - - ++cAuthors; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) - { - hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]); - AtomExitOnFailure(hr, "Failed to parse ATOM category."); - - ++cCategories; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1)) - { - hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry."); - - ++cEntries; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) - { - hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]); - AtomExitOnFailure(hr, "Failed to parse ATOM link."); - - ++cLinks; - } - else - { - hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - - if (!pNewFeed->wzId || !*pNewFeed->wzId) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/id element."); - } - else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/title element."); - } - else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/updated element."); - } - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - ReleaseAtomFeed(pNewFeed); - - return hr; -} - - -/******************************************************************** - AllocateAtomType - allocates enough space for all of the ATOM elements - of a particular type under a particular node. - -*********************************************************************/ -template static HRESULT AllocateAtomType( - __in IXMLDOMNode* pixnParent, - __in LPCWSTR wzT, - __out T** pprgT, - __out DWORD* pcT - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList *pNodeList = NULL; - - long cT = 0; - T* prgT = NULL; - - hr = XmlSelectNodes(pixnParent, wzT, &pNodeList); - AtomExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); - - if (S_OK == hr) - { - hr = pNodeList->get_length(&cT); - AtomExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); - - if (cT == 0) - { - ExitFunction(); - } - - prgT = static_cast(MemAlloc(sizeof(T) * cT, TRUE)); - AtomExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); - - *pcT = cT; - *pprgT = prgT; - prgT = NULL; - } - else - { - *pprgT = NULL; - *pcT = 0; - } - -LExit: - ReleaseMem(prgT); - ReleaseObject(pNodeList); - - return hr; -} - - -/******************************************************************** - ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomAuthor( - __in IXMLDOMNode* pixnAuthor, - __in ATOM_AUTHOR* pAuthor - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - hr = pixnAuthor->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1)) - { - hr = AssignString(&pAuthor->wzName, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM author name."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1)) - { - hr = AssignString(&pAuthor->wzEmail, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM author email."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1)) - { - hr = AssignString(&pAuthor->wzUrl, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM author uri."); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM author elements."); - - hr = S_OK; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - return hr; -} - - -/******************************************************************** - ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomCategory( - __in IXMLDOMNode* pixnCategory, - __in ATOM_CATEGORY* pCategory - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // Process attributes first. - hr = pixnCategory->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); - - while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1)) - { - hr = AssignString(&pCategory->wzLabel, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM category label."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1)) - { - hr = AssignString(&pCategory->wzScheme, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM category scheme."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1)) - { - hr = AssignString(&pCategory->wzTerm, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM category term."); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM category attributes."); - - // Process elements second. - hr = pixnCategory->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM category elements."); - - hr = S_OK; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomContent - parses out an ATOM content from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomContent( - __in IXMLDOMNode* pixnContent, - __in ATOM_CONTENT* pContent - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // Process attributes first. - hr = pixnContent->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); - - while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) - { - hr = AssignString(&pContent->wzType, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM content type."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1)) - { - hr = AssignString(&pContent->wzUrl, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM content scheme."); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM content attributes."); - - // Process elements second. - hr = pixnContent->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM content elements."); - - hr = AssignString(&pContent->wzValue, pixnContent); - AtomExitOnFailure(hr, "Failed to allocate ATOM content value."); - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomEntry( - __in IXMLDOMNode* pixnEntry, - __in ATOM_ENTRY* pEntry - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - DWORD cAuthors = 0; - DWORD cCategories = 0; - DWORD cLinks = 0; - - pEntry->pixn = pixnEntry; - pEntry->pixn->AddRef(); - - // First, allocate all the possible sub elements. - hr = AllocateAtomType(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry authors."); - - hr = AllocateAtomType(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry categories."); - - hr = AllocateAtomType(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry links."); - - // Second, process elements. - hr = pixnEntry->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) - { - hr = AssignString(&pEntry->wzId, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry id."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1)) - { - hr = AssignString(&pEntry->wzSummary, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry summary."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) - { - hr = AssignString(&pEntry->wzTitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry title."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1)) - { - hr = AssignDateTime(&pEntry->ftPublished, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry published."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) - { - hr = AssignDateTime(&pEntry->ftUpdated, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry updated."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) - { - hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry author."); - - ++cAuthors; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) - { - hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry category."); - - ++cCategories; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1)) - { - if (NULL != pEntry->pContent) - { - hr = E_UNEXPECTED; - AtomExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); - } - - pEntry->pContent = static_cast(MemAlloc(sizeof(ATOM_CONTENT), TRUE)); - AtomExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); - - hr = ParseAtomContent(pNode, pEntry->pContent); - AtomExitOnFailure(hr, "Failed to parse ATOM entry content."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) - { - hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry link."); - - ++cLinks; - } - else - { - hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM entry elements."); - - if (!pEntry->wzId || !*pEntry->wzId) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); - } - else if (!pEntry->wzTitle || !*pEntry->wzTitle) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); - } - else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); - } - - hr = S_OK; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomLink - parses out an ATOM link from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomLink( - __in IXMLDOMNode* pixnLink, - __in ATOM_LINK* pLink - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // Process attributes first. - hr = pixnLink->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes for ATOM link."); - - while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1)) - { - hr = AssignString(&pLink->wzRel, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link rel."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1)) - { - hr = AssignString(&pLink->wzUrl, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link href."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1)) - { - hr = XmlGetAttributeLargeNumber(pixnLink, bstrNodeName, &pLink->dw64Length); - if (E_INVALIDARG == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - AtomExitOnFailure(hr, "Failed to parse ATOM link length."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) - { - hr = AssignString(&pLink->wzTitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link title."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) - { - hr = AssignString(&pLink->wzType, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link type."); - } - else - { - hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM link attributes."); - - // Process elements second. - hr = pixnLink->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM link elements."); - - hr = AssignString(&pLink->wzValue, pixnLink); - AtomExitOnFailure(hr, "Failed to allocate ATOM link value."); - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomUnknownElement( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement - ) -{ - Assert(ppUnknownElement); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - ATOM_UNKNOWN_ELEMENT* pNewUnknownElement; - - pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE); - AtomExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - AtomExitOnFailure(hr, "Failed to get unknown element namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - AtomExitOnFailure(hr, "Failed to get unknown element name."); - - hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - AtomExitOnFailure(hr, "Failed to get unknown element value."); - - hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); - - hr = pNode->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); - - while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) - { - hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); - AtomExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); - - ReleaseNullObject(pixnAttribute); - } - - if (S_FALSE == hr) - { - hr = S_OK; - } - AtomExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); - - ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownElement; - pNewUnknownElement = NULL; - -LExit: - FreeAtomUnknownElementList(pNewUnknownElement); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - - return hr; -} - - -/******************************************************************** - ParseAtomUnknownAttribute - parses out attribute from an unknown element - -*********************************************************************/ -static HRESULT ParseAtomUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ) -{ - Assert(ppUnknownAttribute); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; - - pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE); - AtomExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - AtomExitOnFailure(hr, "Failed to get unknown attribute namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - AtomExitOnFailure(hr, "Failed to get unknown attribute name."); - - hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - AtomExitOnFailure(hr, "Failed to get unknown attribute value."); - - hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); - - ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownAttribute; - pNewUnknownAttribute = NULL; - -LExit: - FreeAtomUnknownAttributeList(pNewUnknownAttribute); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - - return hr; -} - - -/******************************************************************** - AssignDateTime - assigns the value of a node to a FILETIME struct. - -*********************************************************************/ -static HRESULT AssignDateTime( - __in FILETIME* pft, - __in IXMLDOMNode* pNode - ) -{ - HRESULT hr = S_OK; - BSTR bstrValue = NULL; - - if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Already process this datetime value."); - } - - hr = XmlGetText(pNode, &bstrValue); - AtomExitOnFailure(hr, "Failed to get value."); - - if (S_OK == hr) - { - hr = TimeFromString3339(bstrValue, pft); - AtomExitOnFailure(hr, "Failed to convert value to time."); - } - else - { - ZeroMemory(pft, sizeof(FILETIME)); - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrValue); - - return hr; -} - - -/******************************************************************** - AssignString - assigns the value of a node to a dynamic string. - -*********************************************************************/ -static HRESULT AssignString( - __out_z LPWSTR* pwzValue, - __in IXMLDOMNode* pNode - ) -{ - HRESULT hr = S_OK; - BSTR bstrValue = NULL; - - if (pwzValue && *pwzValue) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Already processed this value."); - } - - hr = XmlGetText(pNode, &bstrValue); - AtomExitOnFailure(hr, "Failed to get value."); - - if (S_OK == hr) - { - hr = StrAllocString(pwzValue, bstrValue, 0); - AtomExitOnFailure(hr, "Failed to allocate value."); - } - else - { - ReleaseNullStr(pwzValue); - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrValue); - - return hr; -} - - -/******************************************************************** - FreeAtomAuthor - releases all of the memory used by an ATOM author. - -*********************************************************************/ -static void FreeAtomAuthor( - __in_opt ATOM_AUTHOR* pAuthor - ) -{ - if (pAuthor) - { - ReleaseStr(pAuthor->wzUrl); - ReleaseStr(pAuthor->wzEmail); - ReleaseStr(pAuthor->wzName); - } -} - - -/******************************************************************** - FreeAtomCategory - releases all of the memory used by an ATOM category. - -*********************************************************************/ -static void FreeAtomCategory( - __in_opt ATOM_CATEGORY* pCategory - ) -{ - if (pCategory) - { - FreeAtomUnknownElementList(pCategory->pUnknownElements); - - ReleaseStr(pCategory->wzTerm); - ReleaseStr(pCategory->wzScheme); - ReleaseStr(pCategory->wzLabel); - } -} - - -/******************************************************************** - FreeAtomContent - releases all of the memory used by an ATOM content. - -*********************************************************************/ -static void FreeAtomContent( - __in_opt ATOM_CONTENT* pContent - ) -{ - if (pContent) - { - FreeAtomUnknownElementList(pContent->pUnknownElements); - - ReleaseStr(pContent->wzValue); - ReleaseStr(pContent->wzUrl); - ReleaseStr(pContent->wzType); - } -} - - -/******************************************************************** - FreeAtomEntry - releases all of the memory used by an ATOM entry. - -*********************************************************************/ -static void FreeAtomEntry( - __in_opt ATOM_ENTRY* pEntry - ) -{ - if (pEntry) - { - FreeAtomUnknownElementList(pEntry->pUnknownElements); - ReleaseObject(pEntry->pixn); - - for (DWORD i = 0; i < pEntry->cLinks; ++i) - { - FreeAtomLink(pEntry->rgLinks + i); - } - ReleaseMem(pEntry->rgLinks); - - for (DWORD i = 0; i < pEntry->cCategories; ++i) - { - FreeAtomCategory(pEntry->rgCategories + i); - } - ReleaseMem(pEntry->rgCategories); - - for (DWORD i = 0; i < pEntry->cAuthors; ++i) - { - FreeAtomAuthor(pEntry->rgAuthors + i); - } - ReleaseMem(pEntry->rgAuthors); - - FreeAtomContent(pEntry->pContent); - ReleaseMem(pEntry->pContent); - - ReleaseStr(pEntry->wzTitle); - ReleaseStr(pEntry->wzSummary); - ReleaseStr(pEntry->wzId); - } -} - - -/******************************************************************** - FreeAtomLink - releases all of the memory used by an ATOM link. - -*********************************************************************/ -static void FreeAtomLink( - __in_opt ATOM_LINK* pLink - ) -{ - if (pLink) - { - FreeAtomUnknownElementList(pLink->pUnknownElements); - FreeAtomUnknownAttributeList(pLink->pUnknownAttributes); - - ReleaseStr(pLink->wzValue); - ReleaseStr(pLink->wzUrl); - ReleaseStr(pLink->wzType); - ReleaseStr(pLink->wzTitle); - ReleaseStr(pLink->wzRel); - } -} - - -/******************************************************************** - FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements - -*********************************************************************/ -static void FreeAtomUnknownElementList( - __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement - ) -{ - while (pUnknownElement) - { - ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement; - pUnknownElement = pUnknownElement->pNext; - - FreeAtomUnknownAttributeList(pFree->pAttributes); - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzElement); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} - - -/******************************************************************** - FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes - -*********************************************************************/ -static void FreeAtomUnknownAttributeList( - __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ) -{ - while (pUnknownAttribute) - { - ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; - pUnknownAttribute = pUnknownAttribute->pNext; - - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzAttribute); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp deleted file mode 100644 index b6d58cc0..00000000 --- a/src/dutil/buffutil.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define BuffExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) -#define BuffExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) -#define BuffExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) -#define BuffExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) -#define BuffExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUFFUTIL, e, x, s, __VA_ARGS__) -#define BuffExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_BUFFUTIL, g, x, s, __VA_ARGS__) - - -// constants - -#define BUFFER_INCREMENT 128 - - -// helper function declarations - -static HRESULT EnsureBufferSize( - __deref_inout_bcount(cbSize) BYTE** ppbBuffer, - __in SIZE_T cbSize - ); - - -// functions - -extern "C" HRESULT BuffReadNumber( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD* pdw - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pdw); - - HRESULT hr = S_OK; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size."); - - // verify buffer size - if (sizeof(DWORD) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - *pdw = *(const DWORD*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(DWORD); - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadNumber64( - __in_bcount(cbBuffer) const BYTE * pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD64* pdw64 - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pdw64); - - HRESULT hr = S_OK; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size."); - - // verify buffer size - if (sizeof(DWORD64) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(DWORD64); - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadPointer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD_PTR* pdw64 - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pdw64); - - HRESULT hr = S_OK; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size."); - - // verify buffer size - if (sizeof(DWORD_PTR) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - *pdw64 = *(const DWORD_PTR*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(DWORD_PTR); - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadString( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPWSTR* pscz - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pscz); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); - - // verify buffer size - if (sizeof(SIZE_T) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - // read character count - cch = *(const SIZE_T*)(pbBuffer + *piBuffer); - - hr = ::SIZETMult(cch, sizeof(WCHAR), &cb); - BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); - - hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); - BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); - - // verify buffer size - if (cb > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small to hold character data."); - } - - // copy character data - hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch); - BuffExitOnFailure(hr, "Failed to copy character data."); - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadStringAnsi( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPSTR* pscz - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pscz); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); - - // verify buffer size - if (sizeof(SIZE_T) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - // read character count - cch = *(const SIZE_T*)(pbBuffer + *piBuffer); - - hr = ::SIZETMult(cch, sizeof(CHAR), &cb); - BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); - - hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); - BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); - - // verify buffer size - if (cb > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small to hold character count."); - } - - // copy character data - hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch); - BuffExitOnFailure(hr, "Failed to copy character data."); - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadStream( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_inout_bcount(*pcbStream) BYTE** ppbStream, - __out SIZE_T* pcbStream - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(ppbStream); - Assert(pcbStream); - - HRESULT hr = S_OK; - SIZE_T cb = 0; - SIZE_T cbAvailable = 0; - errno_t err = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); - - // verify buffer size - if (sizeof(SIZE_T) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - // read stream size - cb = *(const SIZE_T*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(SIZE_T); - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer."); - - // verify buffer size - if (cb > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small to hold byte count."); - } - - // allocate buffer - *ppbStream = (BYTE*)MemAlloc(cb, TRUE); - BuffExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); - - // read stream data - err = memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, cb); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to read stream from buffer, error: %d", err); - } - - *piBuffer += cb; - - // return stream size - *pcbStream = cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteNumber( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD dw - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy data to buffer - *(DWORD*)(*ppbBuffer + *piBuffer) = dw; - *piBuffer += sizeof(DWORD); - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteNumber64( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD64 dw64 - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy data to buffer - *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64; - *piBuffer += sizeof(DWORD64); - -LExit: - return hr; -} - -extern "C" HRESULT BuffWritePointer( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD_PTR dw - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD_PTR)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy data to buffer - *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; - *piBuffer += sizeof(DWORD_PTR); - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteString( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCWSTR scz - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - errno_t err = 0; - - if (scz) - { - hr = ::StringCchLengthW(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); - BuffExitOnRootFailure(hr, "Failed to get string size.") - } - - cb = cch * sizeof(WCHAR); - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy character count to buffer - *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; - *piBuffer += sizeof(SIZE_T); - - // copy data to buffer - err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%ls', error: %d", scz, err); - } - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteStringAnsi( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCSTR scz - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - errno_t err = 0; - - if (scz) - { - hr = ::StringCchLengthA(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); - BuffExitOnRootFailure(hr, "Failed to get string size.") - } - - cb = cch * sizeof(CHAR); - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy character count to buffer - *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; - *piBuffer += sizeof(SIZE_T); - - // copy data to buffer - err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%hs', error: %d", scz, err); - } - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteStream( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_bcount(cbStream) const BYTE* pbStream, - __in SIZE_T cbStream - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - Assert(pbStream); - - HRESULT hr = S_OK; - SIZE_T cb = cbStream; - errno_t err = 0; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(SIZE_T)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy byte count to buffer - *(SIZE_T*)(*ppbBuffer + *piBuffer) = cb; - *piBuffer += sizeof(SIZE_T); - - // copy data to buffer - err = memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write stream to buffer, error: %d", err); - } - - *piBuffer += cbStream; - -LExit: - return hr; -} - - -// helper functions - -static HRESULT EnsureBufferSize( - __deref_inout_bcount(cbSize) BYTE** ppbBuffer, - __in SIZE_T cbSize - ) -{ - HRESULT hr = S_OK; - SIZE_T cbTarget = ((cbSize / BUFFER_INCREMENT) + 1) * BUFFER_INCREMENT; - - if (*ppbBuffer) - { - if (MemSize(*ppbBuffer) < cbTarget) - { - LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE); - BuffExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer."); - *ppbBuffer = (BYTE*)pv; - } - } - else - { - *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE); - BuffExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); - } - -LExit: - return hr; -} diff --git a/src/dutil/build/WixToolset.DUtil.props b/src/dutil/build/WixToolset.DUtil.props deleted file mode 100644 index 35749be8..00000000 --- a/src/dutil/build/WixToolset.DUtil.props +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) - - - $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) - - - - - $(MSBuildThisFileDirectory)native\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) - - - - - $(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) - - - - - $(MSBuildThisFileDirectory)native\v142\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) - - - diff --git a/src/dutil/butil.cpp b/src/dutil/butil.cpp deleted file mode 100644 index e04b52e9..00000000 --- a/src/dutil/butil.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) -#define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) -#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) -#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) -#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__) - -// constants -// From engine/registration.h -const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; -const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; -const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; - -// Forward declarations. -/******************************************************************** -OpenBundleKey - Opens the bundle uninstallation key for a given bundle - -NOTE: caller is responsible for closing key -********************************************************************/ -static HRESULT OpenBundleKey( - __in_z LPCWSTR wzBundleId, - __in BUNDLE_INSTALL_CONTEXT context, - __inout HKEY *key); - - -extern "C" HRESULT DAPI BundleGetBundleInfo( - __in_z LPCWSTR wzBundleId, - __in_z LPCWSTR wzAttribute, - __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, - __inout_opt LPDWORD pcchValueBuf - ) -{ - Assert(wzBundleId && wzAttribute); - - HRESULT hr = S_OK; - BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE; - LPWSTR sczValue = NULL; - HKEY hkBundle = NULL; - DWORD cchSource = 0; - DWORD dwType = 0; - DWORD dwValue = 0; - - if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute) - { - ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); - } - - if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && - FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) - { - ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); - } - - // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY - hr = RegGetType(hkBundle, wzAttribute, &dwType); - ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); - - switch (dwType) - { - case REG_SZ: - hr = RegReadString(hkBundle, wzAttribute, &sczValue); - ButilExitOnFailure(hr, "Failed to read string property."); - break; - case REG_DWORD: - hr = RegReadNumber(hkBundle, wzAttribute, &dwValue); - ButilExitOnFailure(hr, "Failed to read dword property."); - - hr = StrAllocFormatted(&sczValue, L"%d", dwValue); - ButilExitOnFailure(hr, "Failed to format dword property as string."); - break; - default: - ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); - - } - - hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - ButilExitOnFailure(hr, "Failed to calculate length of string"); - - if (lpValueBuf) - { - // cchSource is the length of the string not including the terminating null character - if (*pcchValueBuf <= cchSource) - { - *pcchValueBuf = ++cchSource; - ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); - } - - hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); - - *pcchValueBuf = cchSource++; - } - -LExit: - ReleaseRegKey(hkBundle); - ReleaseStr(sczValue); - - return hr; -} - -HRESULT DAPI BundleEnumRelatedBundle( - __in_z LPCWSTR wzUpgradeCode, - __in BUNDLE_INSTALL_CONTEXT context, - __inout PDWORD pdwStartIndex, - __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf - ) -{ - HRESULT hr = S_OK; - HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; - HKEY hkUninstall = NULL; - HKEY hkBundle = NULL; - LPWSTR sczUninstallSubKey = NULL; - DWORD cchUninstallSubKey = 0; - LPWSTR sczUninstallSubKeyPath = NULL; - LPWSTR sczValue = NULL; - DWORD dwType = 0; - - LPWSTR* rgsczBundleUpgradeCodes = NULL; - DWORD cBundleUpgradeCodes = 0; - BOOL fUpgradeCodeFound = FALSE; - - if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex) - { - ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); - } - - hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall); - ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); - - for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++) - { - hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey); - ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); - - hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey); - ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); - - hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle); - ButilExitOnFailure(hr, "Failed to open uninstall key path."); - - // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ - hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); - if (FAILED(hr)) - { - ReleaseRegKey(hkBundle); - ReleaseNullStr(sczUninstallSubKey); - ReleaseNullStr(sczUninstallSubKeyPath); - // Not a bundle - continue; - } - - switch (dwType) - { - case REG_SZ: - hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue); - ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1)) - { - *pdwStartIndex = dwIndex; - fUpgradeCodeFound = TRUE; - break; - } - - ReleaseNullStr(sczValue); - - break; - case REG_MULTI_SZ: - hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes); - ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); - - for (DWORD i = 0; i < cBundleUpgradeCodes; i++) - { - LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i]; - if (wzBundleUpgradeCode && *wzBundleUpgradeCode) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1)) - { - *pdwStartIndex = dwIndex; - fUpgradeCodeFound = TRUE; - break; - } - } - } - ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); - - break; - - default: - ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); - - } - - if (fUpgradeCodeFound) - { - if (lpBundleIdBuf) - { - hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast(&cchUninstallSubKey)); - ButilExitOnFailure(hr, "Failed to calculate length of string"); - - hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); - } - - break; - } - - // Cleanup before next iteration - ReleaseRegKey(hkBundle); - ReleaseNullStr(sczUninstallSubKey); - ReleaseNullStr(sczUninstallSubKeyPath); - } - -LExit: - ReleaseStr(sczValue); - ReleaseStr(sczUninstallSubKey); - ReleaseStr(sczUninstallSubKeyPath); - ReleaseRegKey(hkBundle); - ReleaseRegKey(hkUninstall); - ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); - - return hr; -} - - -HRESULT OpenBundleKey( - __in_z LPCWSTR wzBundleId, - __in BUNDLE_INSTALL_CONTEXT context, - __inout HKEY *key) -{ - Assert(key && wzBundleId); - AssertSz(NULL == *key, "*key should be null"); - - HRESULT hr = S_OK; - HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; - LPWSTR sczKeypath = NULL; - - hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); - ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); - - hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); - ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); - -LExit: - ReleaseStr(sczKeypath); - - return hr; -} diff --git a/src/dutil/cabcutil.cpp b/src/dutil/cabcutil.cpp deleted file mode 100644 index 93a9b7e1..00000000 --- a/src/dutil/cabcutil.cpp +++ /dev/null @@ -1,1539 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CabcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) -#define CabcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) -#define CabcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) -#define CabcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) -#define CabcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABCUTIL, e, x, s, __VA_ARGS__) -#define CabcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABCUTIL, g, x, s, __VA_ARGS__) - - -static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?'; -static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024; - -// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder() -// too often (because of duplicates too close together) we theoretically ruin our compression ratio - -// left at zero to maximize install-time performance, because even a small minimum threshhold seems to -// have a high install-time performance cost for little or no size benefit. The value is left here for -// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB. -static const DWORD MINFLUSHTHRESHHOLD = 0; - -// structs -struct MS_CABINET_HEADER -{ - DWORD sig; - DWORD csumHeader; - DWORD cbCabinet; - DWORD csumFolders; - DWORD coffFiles; - DWORD csumFiles; - WORD version; - WORD cFolders; - WORD cFiles; - WORD flags; - WORD setID; - WORD iCabinet; -}; - - -struct MS_CABINET_ITEM -{ - DWORD cbFile; - DWORD uoffFolderStart; - WORD iFolder; - WORD date; - WORD time; - WORD attribs; -}; - -struct CABC_INTERNAL_ADDFILEINFO -{ - LPCWSTR wzSourcePath; - LPCWSTR wzEmptyPath; -}; - -struct CABC_DUPLICATEFILE -{ - DWORD dwFileArrayIndex; - DWORD dwDuplicateCabFileIndex; - LPWSTR pwzSourcePath; - LPWSTR pwzToken; -}; - - -struct CABC_FILE -{ - DWORD dwCabFileIndex; - LPWSTR pwzSourcePath; - LPWSTR pwzToken; - PMSIFILEHASHINFO pmfHash; - LONGLONG llFileSize; - BOOL fHasDuplicates; -}; - - -struct CABC_DATA -{ - LONGLONG llBytesSinceLastFlush; - LONGLONG llFlushThreshhold; - - STRINGDICT_HANDLE shDictHandle; - - WCHAR wzCabinetPath[MAX_PATH]; - WCHAR wzEmptyFile[MAX_PATH]; - HANDLE hEmptyFile; - DWORD dwLastFileIndex; - - DWORD cFilePaths; - DWORD cMaxFilePaths; - CABC_FILE *prgFiles; - - DWORD cDuplicates; - DWORD cMaxDuplicates; - CABC_DUPLICATEFILE *prgDuplicates; - - HRESULT hrLastError; - BOOL fGoodCab; - - HFCI hfci; - ERF erf; - CCAB ccab; - TCOMP tc; - - // Below Field are used for Cabinet Splitting - BOOL fCabinetSplittingEnabled; - FileSplitCabNamesCallback fileSplitCabNamesCallback; - WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting -}; - -const int CABC_HANDLE_BYTES = sizeof(CABC_DATA); - -// -// prototypes -// -static void FreeCabCData( - __in CABC_DATA* pcd - ); -static HRESULT CheckForDuplicateFile( - __in CABC_DATA *pcd, - __out CABC_FILE **ppcf, - __in LPCWSTR wzFileName, - __in PMSIFILEHASHINFO *ppmfHash, - __in LONGLONG llFileSize - ); -static HRESULT AddDuplicateFile( - __in CABC_DATA *pcd, - __in DWORD dwFileArrayIndex, - __in_z LPCWSTR wzSourcePath, - __in_opt LPCWSTR wzToken, - __in DWORD dwDuplicateCabFileIndex - ); -static HRESULT AddNonDuplicateFile( - __in CABC_DATA *pcd, - __in LPCWSTR wzFile, - __in_opt LPCWSTR wzToken, - __in_opt const MSIFILEHASHINFO* pmfHash, - __in LONGLONG llFileSize, - __in DWORD dwCabFileIndex - ); -static HRESULT UpdateDuplicateFiles( - __in const CABC_DATA *pcd - ); -static HRESULT DuplicateFile( - __in MS_CABINET_HEADER *pHeader, - __in const CABC_DATA *pcd, - __in const CABC_DUPLICATEFILE *pDuplicate - ); -static HRESULT UtcFileTimeToLocalDosDateTime( - __in const FILETIME* pFileTime, - __out USHORT* pDate, - __out USHORT* pTime - ); - -static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb); -static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv); -static __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); -static __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); -static __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); -static __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); -static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __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); -static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __inout_bcount(CABC_HANDLE_BYTES) void *pv); - - -/******************************************************************** -CabcBegin - begins creating a cabinet - -NOTE: phContext must be the same handle used in AddFile and Finish. - wzCabDir can be L"", but not NULL. - dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case. - -********************************************************************/ -extern "C" HRESULT DAPI CabCBegin( - __in_z LPCWSTR wzCab, - __in_z LPCWSTR wzCabDir, - __in DWORD dwMaxFiles, - __in DWORD dwMaxSize, - __in DWORD dwMaxThresh, - __in COMPRESSION_TYPE ct, - __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext - ) -{ - Assert(wzCab && *wzCab && phContext); - - HRESULT hr = S_OK; - CABC_DATA *pcd = NULL; - WCHAR wzTempPath[MAX_PATH] = { }; - - C_ASSERT(sizeof(MSIFILEHASHINFO) == 20); - - WCHAR wzPathBuffer [MAX_PATH] = L""; - size_t cchPathBuffer; - if (wzCabDir) - { - hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); - CabcExitOnFailure(hr, "Failed to get length of cab directory"); - - // Need room to terminate with L'\\' and L'\0' - if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer) - { - hr = E_INVALIDARG; - CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); - } - - hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir); - CabcExitOnFailure(hr, "Failed to copy cab directory to buffer"); - - if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) - { - hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\"); - CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer"); - ++cchPathBuffer; - } - } - - pcd = static_cast(MemAlloc(sizeof(CABC_DATA), TRUE)); - CabcExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure"); - - pcd->hrLastError = S_OK; - pcd->fGoodCab = TRUE; - pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD; - - pcd->hEmptyFile = INVALID_HANDLE_VALUE; - - pcd->fileSplitCabNamesCallback = NULL; - - if (NULL == dwMaxSize) - { - pcd->ccab.cb = CAB_MAX_SIZE; - pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired - } - else - { - pcd->ccab.cb = dwMaxSize * 1024 * 1024; - pcd->fCabinetSplittingEnabled = TRUE; - } - - if (0 == dwMaxThresh) - { - // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work. - pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16; - } - else - { - pcd->ccab.cbFolderThresh = dwMaxThresh; - } - - // Translate the compression type - if (COMPRESSION_TYPE_NONE == ct) - { - pcd->tc = tcompTYPE_NONE; - } - else if (COMPRESSION_TYPE_LOW == ct) - { - pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO; - } - else if (COMPRESSION_TYPE_MEDIUM == ct) - { - pcd->tc = TCOMPfromLZXWindow(18); - } - else if (COMPRESSION_TYPE_HIGH == ct) - { - pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI; - } - else if (COMPRESSION_TYPE_MSZIP == ct) - { - pcd->tc = tcompTYPE_MSZIP; - } - else - { - hr = E_INVALIDARG; - CabcExitOnFailure(hr, "Invalid compression type specified."); - } - - if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL)) - { - CabcExitWithLastError(hr, "failed to convert cab name to multi-byte"); - } - - if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) - { - CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte"); - } - - // Remember the path to the cabinet. - hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); - CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); - - hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab); - CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); - - // Get the empty file to use as the blank marker for duplicates. - if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) - { - CabcExitWithLastError(hr, "Failed to get temp path."); - } - - if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile)) - { - CabcExitWithLastError(hr, "Failed to create a temp file name."); - } - - // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) - // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst - // case is we'll leave a zero byte file behind in the temp folder. - 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); - - hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); - CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); - - // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files - if (1 > dwMaxFiles) - { - dwMaxFiles = 1; - } - - pcd->cMaxFilePaths = dwMaxFiles; - size_t cbFileAllocSize = 0; - - hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize)); - CabcExitOnFailure(hr, "Maximum allocation exceeded on initialization."); - - pcd->prgFiles = static_cast(MemAlloc(cbFileAllocSize, TRUE)); - CabcExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files."); - - // Tell cabinet API about our configuration. - pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd); - if (NULL == pcd->hfci || pcd->erf.fError) - { - // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error - if (FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - } - else - { - CabcExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); - } - - pcd->fGoodCab = FALSE; - - 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? - } - - *phContext = pcd; - -LExit: - if (FAILED(hr) && pcd && pcd->hfci) - { - ::FCIDestroy(pcd->hfci); - } - - return hr; -} - - -/******************************************************************** -CabCNextCab - This will be useful when creating multiple cabs. -Haven't needed it yet. -********************************************************************/ -extern "C" HRESULT DAPI CabCNextCab( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ) -{ - UNREFERENCED_PARAMETER(hContext); - // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls - return E_NOTIMPL; -} - - -/******************************************************************** -CabcAddFile - adds a file to a cabinet - -NOTE: hContext must be the same used in Begin and Finish -if wzToken is null, the file's original name is used within the cab -********************************************************************/ -extern "C" HRESULT DAPI CabCAddFile( - __in_z LPCWSTR wzFile, - __in_z_opt LPCWSTR wzToken, - __in_opt PMSIFILEHASHINFO pmfHash, - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ) -{ - Assert(wzFile && *wzFile && hContext); - - HRESULT hr = S_OK; - CABC_DATA *pcd = reinterpret_cast(hContext); - CABC_FILE *pcfDuplicate = NULL; - LONGLONG llFileSize = 0; - PMSIFILEHASHINFO pmfLocalHash = pmfHash; - - // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired - // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled - if(!pcd->fCabinetSplittingEnabled) - { - // Store file size, primarily used to determine which files to hash for duplicates - hr = FileSize(wzFile, &llFileSize); - CabcExitOnFailure(hr, "Failed to check size of file %ls", wzFile); - - hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize); - CabcExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); - } - - if (pcfDuplicate) // This will be null for smart cabbing case - { - DWORD index; - hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index); - CabcExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); - - hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex); - CabcExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); - } - else - { - hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); - CabcExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); - } - - ++pcd->dwLastFileIndex; - -LExit: - // If we allocated a hash struct ourselves, free it - if (pmfHash != pmfLocalHash) - { - ReleaseMem(pmfLocalHash); - } - - return hr; -} - - -/******************************************************************** -CabcFinish - finishes making a cabinet - -NOTE: hContext must be the same used in Begin and AddFile -*********************************************************************/ -extern "C" HRESULT DAPI CabCFinish( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, - __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback - ) -{ - Assert(hContext); - - HRESULT hr = S_OK; - CABC_DATA *pcd = reinterpret_cast(hContext); - CABC_INTERNAL_ADDFILEINFO fileInfo = { }; - DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex - DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array - DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array - LPSTR pszFileToken = NULL; - LONGLONG llFileSize = 0; - - pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback; - - // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile() - // doing so at appropriate times results in install-time performance benefits in the case of duplicate files. - // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump - // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder - // (this is not the same as a regular folder, and is a concept unique to CABs). - - // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files - // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that - // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB - // to close the current folder, and create a new folder for the next file to be added. - - // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence. - // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to") - // 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) - BOOL fFlushBefore = FALSE; - BOOL fFlushAfter = FALSE; - - ReleaseDict(pcd->shDictHandle); - - // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added - for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex) - { - if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file - { - // Just a normal, non-duplicated file. We'll add it to the list for later checking of - // duplicates. - fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath; - fileInfo.wzEmptyPath = NULL; - - // Use the provided token, otherwise default to the source file name. - if (pcd->prgFiles[dwArrayFileIndex].pwzToken) - { - LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken; - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp); - } - else - { - LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp); - } - - if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates) - { - fFlushBefore = TRUE; - } - - llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize; - - ++dwArrayFileIndex; // Increment into the non-duplicate array - } - else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file - { - // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space - // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero - // byte files to point at their duplicated file index. - // - // Notice that duplicate files are not added to the list of file paths because all duplicate - // files point at the same path (the empty file) so there is no point in tracking them with - // their path. - fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath; - fileInfo.wzEmptyPath = pcd->wzEmptyFile; - - // Use the provided token, otherwise default to the source file name. - if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken) - { - LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken; - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp); - } - else - { - LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp); - } - - // 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 - if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) && - !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) && - dwArrayFileIndex < pcd->cFilePaths) - { - fFlushAfter = TRUE; - } - - // We're just adding a 0-byte file, so set it appropriately - llFileSize = 0; - - ++dwDupeArrayFileIndex; // Increment into the duplicate array - } - else // If it's neither duplicate nor non-duplicate, throw an error - { - hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); - CabcExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered"); - } - - if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) - { - if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) - { - CabcExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); - } - pcd->llBytesSinceLastFlush = 0; - } - - pcd->llBytesSinceLastFlush += llFileSize; - - // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct - // through the pointer to an ANSI string. This is neccessary so we can smuggle through the - // path to the empty file (should this be a duplicate file). -#pragma prefast(push) -#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here - if (!::FCIAddFile(pcd->hfci, reinterpret_cast(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc)) -#pragma prefast(pop) - { - pcd->fGoodCab = FALSE; - - // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error - if (FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - } - else - { - 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); - } - - 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? - } - - // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet - // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead - if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - CabcExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet."); - } - - if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) - { - if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) - { - CabcExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); - } - pcd->llBytesSinceLastFlush = 0; - } - - fFlushAfter = FALSE; - fFlushBefore = FALSE; - } - - if (!pcd->fGoodCab) - { - // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error - if (FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - } - else - { - CabcExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); - } - - 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? - } - - // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs) - if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus)) - { - // If we have a last error, use that, otherwise return the useless error - hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL; - 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? - } - - if (pcd->fGoodCab && pcd->cDuplicates) - { - hr = UpdateDuplicateFiles(pcd); - CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); - } - -LExit: - ::FCIDestroy(pcd->hfci); - FreeCabCData(pcd); - ReleaseNullStr(pszFileToken); - - return hr; -} - - -/******************************************************************** -CabCCancel - cancels making a cabinet - -NOTE: hContext must be the same used in Begin and AddFile -*********************************************************************/ -extern "C" void DAPI CabCCancel( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ) -{ - Assert(hContext); - - CABC_DATA* pcd = reinterpret_cast(hContext); - ::FCIDestroy(pcd->hfci); - FreeCabCData(pcd); -} - - -// -// private -// - -static void FreeCabCData( - __in CABC_DATA* pcd - ) -{ - if (pcd) - { - ReleaseFileHandle(pcd->hEmptyFile); - - for (DWORD i = 0; i < pcd->cFilePaths; ++i) - { - ReleaseStr(pcd->prgFiles[i].pwzSourcePath); - ReleaseMem(pcd->prgFiles[i].pmfHash); - } - ReleaseMem(pcd->prgFiles); - ReleaseMem(pcd->prgDuplicates); - - ReleaseMem(pcd); - } -} - -/******************************************************************** - SmartCab functions - -********************************************************************/ - -static HRESULT CheckForDuplicateFile( - __in CABC_DATA *pcd, - __out CABC_FILE **ppcf, - __in LPCWSTR wzFileName, - __in PMSIFILEHASHINFO *ppmfHash, - __in LONGLONG llFileSize - ) -{ - DWORD i; - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - - CabcExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file"); - CabcExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file"); - - *ppcf = NULL; // By default, we'll set our output to NULL - - hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast(ppcf)); - // If we found it in the hash of previously added source paths, return our match immediately - if (SUCCEEDED(hr)) - { - ExitFunction1(hr = S_OK); - } - else if (E_NOTFOUND == hr) - { - hr = S_OK; - } - CabcExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files"); - - for (i = 0; i < pcd->cFilePaths; ++i) - { - // If two files have the same size, use hashing to check if they're a match - if (llFileSize == pcd->prgFiles[i].llFileSize) - { - // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it - if (pcd->prgFiles[i].pmfHash == NULL) - { - pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - CabcExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash"); - - pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); - er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash); - CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath); - } - - // If our own file hasn't yet been hashed, hash it - if (NULL == *ppmfHash) - { - *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - CabcExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash"); - - (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); - er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash); - CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath); - } - - // If the two file hashes are both of the expected size, and they match, we've got a match, so return it! - if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize && - sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize && - pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] && - pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] && - pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] && - pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3]) - { - *ppcf = pcd->prgFiles + i; - ExitFunction1(hr = S_OK); - } - } - } - -LExit: - - return hr; -} - - -static HRESULT AddDuplicateFile( - __in CABC_DATA *pcd, - __in DWORD dwFileArrayIndex, - __in_z LPCWSTR wzSourcePath, - __in_opt LPCWSTR wzToken, - __in DWORD dwDuplicateCabFileIndex - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - - // Ensure there is enough memory to store this duplicate file index. - if (pcd->cDuplicates == pcd->cMaxDuplicates) - { - pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?) - size_t cbDuplicates = 0; - - hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates); - CabcExitOnFailure(hr, "Maximum allocation exceeded."); - - if (pcd->cDuplicates) - { - pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE); - CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file."); - } - else - { - pv = MemAlloc(cbDuplicates, FALSE); - CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file."); - } - - ZeroMemory(reinterpret_cast(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE)); - - pcd->prgDuplicates = static_cast(pv); - pv = NULL; - } - - // Store the duplicate file index. - pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex; - pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex; - pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates - - hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0); - CabcExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath); - - if (wzToken && *wzToken) - { - hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0); - CabcExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken); - } - - ++pcd->cDuplicates; - -LExit: - ReleaseMem(pv); - return hr; -} - - -static HRESULT AddNonDuplicateFile( - __in CABC_DATA *pcd, - __in LPCWSTR wzFile, - __in_opt LPCWSTR wzToken, - __in_opt const MSIFILEHASHINFO* pmfHash, - __in LONGLONG llFileSize, - __in DWORD dwCabFileIndex - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - - // Ensure there is enough memory to store this file index. - if (pcd->cFilePaths == pcd->cMaxFilePaths) - { - pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?) - size_t cbFilePaths = 0; - - hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths); - CabcExitOnFailure(hr, "Maximum allocation exceeded."); - - pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE); - CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file."); - - ZeroMemory(reinterpret_cast(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE)); - - pcd->prgFiles = static_cast(pv); - pv = NULL; - } - - // Store the file index information. - // TODO: add this to a sorted list so we can do a binary search later. - CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths; - pcf->dwCabFileIndex = dwCabFileIndex; - pcf->llFileSize = llFileSize; - - if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize) - { - pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - CabcExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash"); - - pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); - pcf->pmfHash->dwData[0] = pmfHash->dwData[0]; - pcf->pmfHash->dwData[1] = pmfHash->dwData[1]; - pcf->pmfHash->dwData[2] = pmfHash->dwData[2]; - pcf->pmfHash->dwData[3] = pmfHash->dwData[3]; - } - - hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0); - CabcExitOnFailure(hr, "Failed to copy file path: %ls", wzFile); - - if (wzToken && *wzToken) - { - hr = StrAllocString(&pcf->pwzToken, wzToken, 0); - CabcExitOnFailure(hr, "Failed to copy file token: %ls", wzToken); - } - - ++pcd->cFilePaths; - - hr = DictAddValue(pcd->shDictHandle, pcf); - CabcExitOnFailure(hr, "Failed to add file to dictionary of added files"); - -LExit: - ReleaseMem(pv); - return hr; -} - - -static HRESULT UpdateDuplicateFiles( - __in const CABC_DATA *pcd - ) -{ - HRESULT hr = S_OK; - DWORD cbCabinet = 0; - LARGE_INTEGER liCabinetSize = { }; - HANDLE hCabinet = INVALID_HANDLE_VALUE; - HANDLE hCabinetMapping = NULL; - LPVOID pv = NULL; - MS_CABINET_HEADER *pCabinetHeader = NULL; - - hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hCabinet) - { - CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); - } - - // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as - // the upper bound for the memory map. - if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) - { - CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); - } - - if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) - { - cbCabinet = liCabinetSize.LowPart; - } - else - { - cbCabinet = MAX_CABINET_HEADER_SIZE; - } - - // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE - hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); - if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) - { - CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); - } - - pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); - CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); - - pCabinetHeader = static_cast(pv); - - for (DWORD i = 0; i < pcd->cDuplicates; ++i) - { - const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i; - - hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile); - CabcExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex); - } - -LExit: - if (pv) - { - ::UnmapViewOfFile(pv); - } - if (hCabinetMapping) - { - ::CloseHandle(hCabinetMapping); - } - ReleaseFileHandle(hCabinet); - - return hr; -} - - -static HRESULT DuplicateFile( - __in MS_CABINET_HEADER *pHeader, - __in const CABC_DATA *pcd, - __in const CABC_DUPLICATEFILE *pDuplicate - ) -{ - HRESULT hr = S_OK; - BYTE *pbHeader = reinterpret_cast(pHeader); - BYTE* pbItem = pbHeader + pHeader->coffFiles; - const MS_CABINET_ITEM *pOriginalItem = NULL; - MS_CABINET_ITEM *pDuplicateItem = NULL; - - if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex || - pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex || - pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex) - { - hr = E_UNEXPECTED; - CabcExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex); - } - - // Step through each cabinet items until we get to the original - // file's index. Notice that the name of the cabinet item is - // appended to the end of the MS_CABINET_INFO, that's why we can't - // index straight to the data we want. - for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i) - { - LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); - pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; - } - - pOriginalItem = reinterpret_cast(pbItem); - - // Now pick up where we left off after the original file's index - // was found and loop until we find the duplicate file's index. - for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i) - { - LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); - pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; - } - - pDuplicateItem = reinterpret_cast(pbItem); - - if (0 != pDuplicateItem->cbFile) - { - hr = E_UNEXPECTED; - CabcExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile); - } - - pDuplicateItem->cbFile = pOriginalItem->cbFile; - pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart; - pDuplicateItem->iFolder = pOriginalItem->iFolder; - // Note: we do *not* duplicate the date/time and attributes metadata from - // the original item to the duplicate. The following lines are commented - // so people are not tempted to put them back. - //pDuplicateItem->date = pOriginalItem->date; - //pDuplicateItem->time = pOriginalItem->time; - //pDuplicateItem->attribs = pOriginalItem->attribs; - -LExit: - return hr; -} - - -static HRESULT UtcFileTimeToLocalDosDateTime( - __in const FILETIME* pFileTime, - __out USHORT* pDate, - __out USHORT* pTime - ) -{ - HRESULT hr = S_OK; - FILETIME ftLocal = { }; - - if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal)) - { - CabcExitWithLastError(hr, "Filed to convert file time to local file time."); - } - - if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime)) - { - CabcExitWithLastError(hr, "Filed to convert file time to DOS date time."); - } - -LExit: - return hr; -} - - -/******************************************************************** - FCI callback functions - -*********************************************************************/ -static __callback int DIAMONDAPI CabCFilePlaced( - __in PCCAB pccab, - __in_z PSTR szFile, - __in long cbFile, - __in BOOL fContinuation, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(pccab); - UNREFERENCED_PARAMETER(szFile); - UNREFERENCED_PARAMETER(cbFile); - UNREFERENCED_PARAMETER(fContinuation); - UNREFERENCED_PARAMETER(pv); - return 0; -} - - -static __callback void * DIAMONDAPI CabCAlloc( - __in ULONG cb - ) -{ - return MemAlloc(cb, FALSE); -} - - -static __callback void DIAMONDAPI CabCFree( - __out_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - MemFree(pv); -} - -static __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 - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - INT_PTR pFile = -1; - DWORD dwAccess = 0; - DWORD dwDisposition = 0; - DWORD dwAttributes = 0; - - // - // Translate flags for CreateFile - // - if (oflag & _O_CREAT) - { - if (pmode == _S_IREAD) - dwAccess |= GENERIC_READ; - else if (pmode == _S_IWRITE) - dwAccess |= GENERIC_WRITE; - else if (pmode == (_S_IWRITE | _S_IREAD)) - dwAccess |= GENERIC_READ | GENERIC_WRITE; - - if (oflag & _O_SHORT_LIVED) - dwDisposition = FILE_ATTRIBUTE_TEMPORARY; - else if (oflag & _O_TEMPORARY) - dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE; - else if (oflag & _O_EXCL) - dwDisposition = CREATE_NEW; - } - if (oflag & _O_TRUNC) - dwDisposition = CREATE_ALWAYS; - - if (!dwAccess) - dwAccess = GENERIC_READ; - if (!dwDisposition) - dwDisposition = OPEN_EXISTING; - if (!dwAttributes) - dwAttributes = FILE_ATTRIBUTE_NORMAL; - - // Check to see if we were passed the magic character that says 'Unicode string follows'. - if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile) - { - pFile = reinterpret_cast(::CreateFileW(reinterpret_cast(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); - } - else - { -#pragma prefast(push) -#pragma prefast(disable:25068) // We intentionally don't use the unicode API here - pFile = reinterpret_cast(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); -#pragma prefast(pop) - } - - if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) - { - CabcExitOnLastError(hr, "failed to open file: %s", pszFile); - } - -LExit: - if (FAILED(hr)) - pcd->hrLastError = *err = hr; - - return FAILED(hr) ? -1 : pFile; -} - - -static __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 - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - DWORD cbRead = 0; - - CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided"); - if (!::ReadFile(reinterpret_cast(hf), memory, cb, &cbRead, NULL)) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to read during cabinet extraction"); - } - -LExit: - if (FAILED(hr)) - { - pcd->hrLastError = *err = hr; - } - - return FAILED(hr) ? -1 : cbRead; -} - - -static __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 - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - DWORD cbWrite = 0; - - CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided"); - if (!::WriteFile(reinterpret_cast(hf), memory, cb, &cbWrite, NULL)) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to write during cabinet extraction"); - } - -LExit: - if (FAILED(hr)) - pcd->hrLastError = *err = hr; - - return FAILED(hr) ? -1 : cbWrite; -} - - -static __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 - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - DWORD dwMoveMethod; - LONG lMove = 0; - - switch (seektype) - { - case 0: // SEEK_SET - dwMoveMethod = FILE_BEGIN; - break; - case 1: /// SEEK_CUR - dwMoveMethod = FILE_CURRENT; - break; - case 2: // SEEK_END - dwMoveMethod = FILE_END; - break; - default : - dwMoveMethod = 0; - hr = E_UNEXPECTED; - CabcExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype); - } - - // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. - // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) - // Todo: update these comments for FCI (are they accurate for FCI as well?) - lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); - if (DWORD_MAX == lMove) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to move file pointer %d bytes", dist); - } - -LExit: - if (FAILED(hr)) - { - pcd->hrLastError = *err = hr; - } - - return FAILED(hr) ? -1 : lMove; -} - - -static __callback int FAR DIAMONDAPI CabCClose( - __in INT_PTR hf, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - - if (!::CloseHandle(reinterpret_cast(hf))) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to close file during cabinet extraction"); - } - -LExit: - if (FAILED(hr)) - { - pcd->hrLastError = *err = hr; - } - - return FAILED(hr) ? -1 : 0; -} - -static __callback int DIAMONDAPI CabCDelete( - __in_z PSTR szFile, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(err); - UNREFERENCED_PARAMETER(pv); - -#pragma prefast(push) -#pragma prefast(disable:25068) // We intentionally don't use the unicode API here - ::DeleteFileA(szFile); -#pragma prefast(pop) - - return 0; -} - - -__success(return != FALSE) -static __callback BOOL DIAMONDAPI CabCGetTempFile( - __out_bcount_z(cbFile) char *szFile, - __in int cbFile, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - static volatile DWORD dwIndex = 0; - - HRESULT hr = S_OK; - char szTempPath[MAX_PATH] = { }; - DWORD cchTempPath = MAX_PATH; - DWORD dwProcessId = ::GetCurrentProcessId(); - HANDLE hTempFile = INVALID_HANDLE_VALUE; - - if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) - { - CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation."); - } - - for (DWORD i = 0; i < DWORD_MAX; ++i) - { - LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast(&dwIndex)); - - hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); - CabcExitOnFailure(hr, "failed to format log file path."); - - hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (INVALID_HANDLE_VALUE != hTempFile) - { - // we found one that doesn't exist - hr = S_OK; - break; - } - else - { - hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. - } - } - CabcExitOnFailure(hr, "failed to find temporary file."); - -LExit: - ReleaseFileHandle(hTempFile); - - if (FAILED(hr)) - { - pcd->hrLastError = hr; - } - - return FAILED(hr)? FALSE : TRUE; -} - - -__success(return != FALSE) -static __callback BOOL DIAMONDAPI CabCGetNextCabinet( - __in PCCAB pccab, - __in ULONG ul, - __out_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(ul); - - // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - LPWSTR pwzFileToken = NULL; - WCHAR wzNewCabName[MAX_PATH] = L""; - - if (pccab->iCab == 1) - { - pcd->wzFirstCabinetName[0] = '\0'; - LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath); - size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName)); - if (len > 4) - { - len -= 4; // remove Extention ".cab" of 8.3 Format - } - hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len); - CabcExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name"); - } - - const int nAlphabets = 26; // Number of Alphabets from a to z - if (pccab->iCab <= nAlphabets) - { - // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ - hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - } - else if (pccab->iCab <= nAlphabets*nAlphabets) - { - // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ...... - int char2 = (pccab->iCab) % nAlphabets; - int char1 = (pccab->iCab - char2)/nAlphabets; - if (char2 == 0) - { - // e.g. when iCab = 52, we want az - char2 = nAlphabets; // Second char must be 'z' in this case - char1--; // First Char must be decremented by 1 - } - hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - } - else - { - hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index. - CabcExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name"); - } - - // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method - if(pcd->fileSplitCabNamesCallback != 0) - { - // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split - // This code will need updation if we need to send all file tokens for the splitting Cabinets - if (pcd->prgFiles[0].pwzToken) - { - pwzFileToken = pcd->prgFiles[0].pwzToken; - } - else - { - LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath; - pwzFileToken = FileFromPath(wzSourcePath); - } - - // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table - pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken); - } - -LExit: - if (FAILED(hr)) - { - // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!! - // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting - pcd->hrLastError = hr; - return FALSE; - } - else - { - return TRUE; - } -} - - -static __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 - ) -{ - HRESULT hr = S_OK; - CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast(pszName); - LPCWSTR wzFile = NULL; - DWORD cbFile = 0; - LPSTR pszFilePlusMagic = NULL; - DWORD cbFilePlusMagic = 0; - WIN32_FILE_ATTRIBUTE_DATA fad = { }; - INT_PTR iResult = -1; - - // If there is an empty file provided, use that as the source path to cab (since we - // must be dealing with a duplicate file). Otherwise, use the source path you'd expect. - wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath; - cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR); - - // Convert the source file path into an Ansi string that our APIs will recognize as - // a Unicode string (due to the magic character). - cbFilePlusMagic = cbFile + 1; // add one for the magic. - pszFilePlusMagic = reinterpret_cast(MemAlloc(cbFilePlusMagic, TRUE)); - - *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER; - memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile); - - if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad)) - { - CabcExitWithLastError(hr, "Failed to get file attributes on '%ls'.", pFileInfo->wzSourcePath); - } - - // Set the attributes but only allow the few attributes that CAB supports. - *pattribs = static_cast(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE); - - hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime); - if (FAILED(hr)) - { - // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were - // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't - // found. This would create further problems if the file was written to the CAB without this value. Windows - // Installer would then fail to extract the file. - hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime); - CabcExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName); - } - - iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv); - -LExit: - ReleaseMem(pszFilePlusMagic); - if (FAILED(hr)) - { - *err = (int)hr; - } - - return FAILED(hr) ? -1 : iResult; -} - - -static __callback long DIAMONDAPI CabCStatus( - __in UINT ui, - __in ULONG cb1, - __in ULONG cb2, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(ui); - UNREFERENCED_PARAMETER(cb1); - UNREFERENCED_PARAMETER(cb2); - UNREFERENCED_PARAMETER(pv); - return 0; -} diff --git a/src/dutil/cabutil.cpp b/src/dutil/cabutil.cpp deleted file mode 100644 index 5d77e483..00000000 --- a/src/dutil/cabutil.cpp +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CabExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) -#define CabExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) -#define CabExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) -#define CabExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) -#define CabExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABUTIL, e, x, s, __VA_ARGS__) -#define CabExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABUTIL, g, x, s, __VA_ARGS__) - - -// external prototypes -typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*); -typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); -typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO); -typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *); - - -// -// static globals -// -static HMODULE vhCabinetDll = NULL; - -static HFDI vhfdi = NULL; -static PFNFDICREATE vpfnFDICreate = NULL; -static PFNFDICOPY vpfnFDICopy = NULL; -static PFNFDIISCABINET vpfnFDIIsCabinet = NULL; -static PFNFDIDESTROY vpfnFDIDestroy = NULL; -static ERF verf; - -static DWORD64 vdw64EmbeddedOffset = 0; - -// -// structs -// -struct CAB_CALLBACK_STRUCT -{ - BOOL fStopExtracting; // flag set when no more files are needed - LPCWSTR pwzExtract; // file to extract ("*" means extract all) - LPCWSTR pwzExtractDir; // directory to extract files to - - // possible user data - CAB_CALLBACK_PROGRESS pfnProgress; - LPVOID pvContext; -}; - -// -// prototypes -// -static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize); -static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData); -static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode); -static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb); -static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb); -static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf); -static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype); -static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify); -static 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); - -static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL; - - -inline HRESULT LoadCabinetDll() -{ - HRESULT hr = S_OK; - if (!vhCabinetDll) - { - hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll); - CabExitOnFailure(hr, "failed to load cabinet.dll"); - - // retrieve all address functions - vpfnFDICreate = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICreate")); - CabExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL"); - vpfnFDICopy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICopy")); - CabExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL"); - vpfnFDIIsCabinet = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIIsCabinet")); - CabExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL"); - vpfnFDIDestroy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIDestroy")); - CabExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL"); - - vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf); - CabExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll"); - } - -LExit: - if (FAILED(hr) && vhCabinetDll) - { - ::FreeLibrary(vhCabinetDll); - vhCabinetDll = NULL; - } - - return hr; -} - - -static HANDLE OpenFileWithRetry( - __in LPCWSTR wzPath, - __in DWORD dwDesiredAccess, - __in DWORD dwCreationDisposition -) -{ - HANDLE hFile = INVALID_HANDLE_VALUE; - - for (DWORD i = 0; i < 30; ++i) - { - hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE != hFile) - { - break; - } - - ::Sleep(100); - } - - return hFile; -} - - -/******************************************************************** - CabInitialize - initializes internal static variables - -********************************************************************/ -extern "C" HRESULT DAPI CabInitialize( - __in BOOL fDelayLoad - ) -{ - HRESULT hr = S_OK; - - if (!fDelayLoad) - { - hr = LoadCabinetDll(); - CabExitOnFailure(hr, "failed to load CABINET.DLL"); - } - -LExit: - return hr; -} - - -/******************************************************************** - CabUninitialize - initializes internal static variables - -********************************************************************/ -extern "C" void DAPI CabUninitialize( - ) -{ - if (vhfdi) - { - if (vpfnFDIDestroy) - { - vpfnFDIDestroy(vhfdi); - } - vhfdi = NULL; - } - - vpfnFDICreate = NULL; - vpfnFDICopy =NULL; - vpfnFDIIsCabinet = NULL; - vpfnFDIDestroy = NULL; - - if (vhCabinetDll) - { - ::FreeLibrary(vhCabinetDll); - vhCabinetDll = NULL; - } -} - -/******************************************************************** - CabEnumerate - list files inside cabinet - - NOTE: wzCabinet must be full path to cabinet file - pfnNotify is callback function to get notified for each file - in the cabinet -********************************************************************/ -extern "C" HRESULT DAPI CabEnumerate( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzEnumerateFile, - __in STDCALL_PFNFDINOTIFY pfnNotify, - __in DWORD64 dw64EmbeddedOffset - ) -{ - return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset); -} - -/******************************************************************** - CabExtract - extracts one or all files from a cabinet - - NOTE: wzCabinet must be full path to cabinet file - wzExtractFile can be a single file id or "*" to extract all files - wzExttractDir must be normalized (end in a "\") - if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa -********************************************************************/ -extern "C" HRESULT DAPI CabExtract( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzExtractFile, - __in_z LPCWSTR wzExtractDir, - __in_opt CAB_CALLBACK_PROGRESS pfnProgress, - __in_opt LPVOID pvContext, - __in DWORD64 dw64EmbeddedOffset - ) -{ - return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset); -} - -// -// private -// -/******************************************************************** - FDINotify -- wrapper that converts call convention from __cdecl to __stdcall. - - NOTE: Since netfx 1.1 supports only function pointers (delegates) - with __stdcall calling convention and cabinet api uses - __cdecl calling convention, we need this wrapper function. - netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate. - TODO: remove this when upgrading to netfx 2.0. -********************************************************************/ -static __callback INT_PTR DIAMONDAPI FDINotify( - __in FDINOTIFICATIONTYPE iNotification, - __inout FDINOTIFICATION *pFDINotify - ) -{ - if (NULL != v_pfnNetFx11Notify) - { - return v_pfnNetFx11Notify(iNotification, pFDINotify); - } - else - { - return (INT_PTR)0; - } -} - - -/******************************************************************** - CabOperation - helper function that enumerates or extracts files - from cabinet - - NOTE: wzCabinet must be full path to cabinet file - wzExtractFile can be a single file id or "*" to extract all files - wzExttractDir must be normalized (end in a "\") - if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa - pfnNotify is callback function to get notified for each file - in the cabinet. If it's NULL, files will be extracted. -********************************************************************/ -static 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 - ) -{ - HRESULT hr = S_OK; - BOOL fResult; - - LPWSTR sczCabinet = NULL; - LPWSTR pwz = NULL; - CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings - CHAR szCabFile[MAX_PATH * 4]; - - CAB_CALLBACK_STRUCT ccs; - PFNFDINOTIFY pfnFdiNotify; - - // - // ensure the cabinet.dll is loaded - // - if (!vhfdi) - { - hr = LoadCabinetDll(); - CabExitOnFailure(hr, "failed to load CABINET.DLL"); - } - - hr = StrAllocString(&sczCabinet, wzCabinet, 0); - CabExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet); - - // - // split the cabinet full path into directory and filename and convert to multi-byte (ick!) - // - pwz = FileFromPath(sczCabinet); - CabExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet); - - if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL)) - { - CabExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz); - } - - *pwz = '\0'; - - // If a full path was not provided, use the relative current directory. - if (wzCabinet == pwz) - { - hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); - CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); - } - else - { - if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) - { - CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); - } - } - - // - // iterate through files in cabinet extracting them to the callback function - // - ccs.fStopExtracting = FALSE; - ccs.pwzExtract = wzExtractFile; - ccs.pwzExtractDir = wzExtractDir; - ccs.pfnProgress = pfnProgress; - ccs.pvContext = pvContext; - - vdw64EmbeddedOffset = dw64EmbeddedOffset; - - // if pfnNotify is given, use it, otherwise use default callback - if (NULL == pfnNotify) - { - pfnFdiNotify = CabExtractCallback; - } - else - { - v_pfnNetFx11Notify = pfnNotify; - pfnFdiNotify = FDINotify; - } - fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); - if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure - { - CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); - } - -LExit: - ReleaseStr(sczCabinet); - v_pfnNetFx11Notify = NULL; - - return hr; -} - -/**************************************************************************** - default extract routines - -****************************************************************************/ -static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize) -{ - return MemAlloc(dwSize, FALSE); -} - - -static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData) -{ - MemFree(pvData); -} - - -static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - INT_PTR pFile = -1; - LPWSTR sczCabFile = NULL; - - // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail - if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) - { - hr = E_OUTOFMEMORY; - CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); - } - - hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); - CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); - - hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING); - if (INVALID_HANDLE_VALUE == hFile) - { - CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile); - } - - pFile = reinterpret_cast(hFile); - - if (vdw64EmbeddedOffset) - { - hr = CabExtractSeek(pFile, 0, 0); - CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); - } - - hFile = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hFile); - ReleaseStr(sczCabFile); - - return FAILED(hr) ? -1 : pFile; -} - - -static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb) -{ - HRESULT hr = S_OK; - DWORD cbRead = 0; - - CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read"); - if (!::ReadFile(reinterpret_cast(hf), pv, cb, &cbRead, NULL)) - { - CabExitWithLastError(hr, "failed to read during cabinet extraction"); - } - -LExit: - return FAILED(hr) ? -1 : cbRead; -} - - -static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb) -{ - HRESULT hr = S_OK; - DWORD cbWrite = 0; - - CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write"); - if (!::WriteFile(reinterpret_cast(hf), pv, cb, &cbWrite, NULL)) - { - CabExitWithLastError(hr, "failed to write during cabinet extraction"); - } - -LExit: - return FAILED(hr) ? -1 : cbWrite; -} - - -static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype) -{ - HRESULT hr = S_OK; - DWORD dwMoveMethod; - LONG lMove = 0; - - switch (seektype) - { - case 0: // SEEK_SET - dwMoveMethod = FILE_BEGIN; - dist += static_cast(vdw64EmbeddedOffset); - break; - case 1: /// SEEK_CUR - dwMoveMethod = FILE_CURRENT; - break; - case 2: // SEEK_END - dwMoveMethod = FILE_END; - break; - default : - dwMoveMethod = 0; - hr = E_UNEXPECTED; - CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); - } - - // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. - // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) - lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); - if (0xFFFFFFFF == lMove) - { - CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist); - } - -LExit: - return FAILED(hr) ? -1 : lMove - static_cast(vdw64EmbeddedOffset); -} - - -static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf) -{ - HRESULT hr = S_OK; - - if (!::CloseHandle(reinterpret_cast(hf))) - { - CabExitWithLastError(hr, "failed to close file during cabinet extraction"); - } - -LExit: - return FAILED(hr) ? -1 : 0; -} - - -static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify) -{ - Assert(pFDINotify->pv); - - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - INT_PTR ipResult = 0; // result to return on success - - CAB_CALLBACK_STRUCT* pccs = static_cast(pFDINotify->pv); - LPCSTR sz; - WCHAR wz[MAX_PATH]; - FILETIME ft; - - switch (iNotification) - { - case fdintCOPY_FILE: // begin extracting a resource from cabinet - CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); - CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); - - if (pccs->fStopExtracting) - { - ExitFunction1(hr = S_FALSE); // no more extracting - } - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - if (pccs->pfnProgress) - { - hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); - if (S_OK != hr) - { - ExitFunction(); - } - } - - if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) - { - // get the created date for the resource in the cabinet - FILETIME ftLocal; - if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) - { - CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); - } - ::LocalFileTimeToFileTime(&ftLocal, &ft); - - WCHAR wzPath[MAX_PATH]; - hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); - CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); - hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); - - hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS); - if (INVALID_HANDLE_VALUE == hFile) - { - CabExitWithLastError(hr, "failed to create file: %ls", wzPath); - } - - ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) - - if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) - { - if (::SetEndOfFile(hFile)) - { - ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer - } - } - - ipResult = reinterpret_cast(hFile); - hFile = INVALID_HANDLE_VALUE; - } - else // resource wasn't requested, skip it - { - hr = S_OK; - ipResult = 0; - } - - break; - case fdintCLOSE_FILE_INFO: // resource extraction complete - Assert(pFDINotify->hf && pFDINotify->psz1); - CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); - - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - if (NULL != pFDINotify->hf) // just close the file - { - ::CloseHandle(reinterpret_cast(pFDINotify->hf)); - } - - if (pccs->pfnProgress) - { - hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); - } - - if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going - { - ipResult = TRUE; - } - else // something went wrong or we only needed to extract one file - { - hr = S_OK; - ipResult = FALSE; - pccs->fStopExtracting = TRUE; - } - - break; - case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through - case fdintNEXT_CABINET: __fallthrough; - case fdintENUMERATE: __fallthrough; - case fdintCABINET_INFO: - break; - default: - AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); - }; - -LExit: - ReleaseFileHandle(hFile); - - return (S_OK == hr) ? ipResult : -1; -} diff --git a/src/dutil/certutil.cpp b/src/dutil/certutil.cpp deleted file mode 100644 index 69897b9e..00000000 --- a/src/dutil/certutil.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CertExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) -#define CertExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) -#define CertExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) -#define CertExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) -#define CertExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CERTUTIL, e, x, s, __VA_ARGS__) -#define CertExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CERTUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -CertReadProperty - reads a property from the certificate. - -NOTE: call MemFree() on the returned pvValue. -********************************************************************/ -extern "C" HRESULT DAPI CertReadProperty( - __in PCCERT_CONTEXT pCertContext, - __in DWORD dwProperty, - __deref_out_bound LPVOID* ppvValue, - __out_opt DWORD* pcbValue - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - DWORD cb = 0; - - if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb)) - { - CertExitWithLastError(hr, "Failed to get size of certificate property."); - } - - pv = MemAlloc(cb, TRUE); - CertExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property."); - - if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb)) - { - CertExitWithLastError(hr, "Failed to get certificate property."); - } - - *ppvValue = pv; - pv = NULL; - - if (pcbValue) - { - *pcbValue = cb; - } - -LExit: - ReleaseMem(pv); - return hr; -} - - -extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp( - __in CMSG_SIGNER_INFO* pSignerInfo, - __out FILETIME* pftSigningTimestamp - ) -{ - HRESULT hr = S_OK; - CRYPT_INTEGER_BLOB* pBlob = NULL; - PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; - DWORD cbSigningTimestamp = sizeof(FILETIME); - - // Find the countersigner blob. The countersigner in Authenticode contains the time - // that signing took place. It's a "countersigner" because the signing time was sent - // off to the certificate authority in the sky to return the verified time signed. - for (DWORD i = 0; i < pSignerInfo->UnauthAttrs.cAttr; ++i) - { - if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_counterSign, -1, pSignerInfo->UnauthAttrs.rgAttr[i].pszObjId, -1)) - { - pBlob = pSignerInfo->UnauthAttrs.rgAttr[i].rgValue; - break; - } - } - - if (!pBlob) - { - hr = TRUST_E_FAIL; - CertExitOnFailure(hr, "Failed to find countersigner in signer information."); - } - - hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast(&pCounterSignerInfo), NULL); - CertExitOnFailure(hr, "Failed to decode countersigner information."); - - pBlob = NULL; // reset the blob before searching for the signing time. - - // Find the signing time blob in the countersigner. - for (DWORD i = 0; i < pCounterSignerInfo->AuthAttrs.cAttr; ++i) - { - if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_signingTime, -1, pCounterSignerInfo->AuthAttrs.rgAttr[i].pszObjId, -1)) - { - pBlob = pCounterSignerInfo->AuthAttrs.rgAttr[i].rgValue; - break; - } - } - - if (!pBlob) - { - hr = TRUST_E_FAIL; - CertExitOnFailure(hr, "Failed to find signing time in countersigner information."); - } - - if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp)) - { - CertExitWithLastError(hr, "Failed to decode countersigner signing timestamp."); - } - -LExit: - ReleaseMem(pCounterSignerInfo); - - return hr; -} - - -extern "C" HRESULT DAPI GetCryptProvFromCert( - __in_opt HWND hwnd, - __in PCCERT_CONTEXT pCert, - __out HCRYPTPROV *phCryptProv, - __out DWORD *pdwKeySpec, - __in BOOL *pfDidCryptAcquire, - __deref_opt_out LPWSTR *ppwszTmpContainer, - __deref_opt_out LPWSTR *ppwszProviderName, - __out DWORD *pdwProviderType - ) -{ - HRESULT hr = S_OK; - HMODULE hMsSign32 = NULL; - - typedef BOOL (WINAPI *GETCRYPTPROVFROMCERTPTR)(HWND, PCCERT_CONTEXT, HCRYPTPROV*, DWORD*,BOOL*,LPWSTR*,LPWSTR*,DWORD*); - GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL; - - hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); - CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); - - pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert"); - CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); - - if (!pGetCryptProvFromCert(hwnd, - pCert, - phCryptProv, - pdwKeySpec, - pfDidCryptAcquire, - ppwszTmpContainer, - ppwszProviderName, - pdwProviderType)) - { - CertExitWithLastError(hr, "Failed to get CSP from cert."); - } -LExit: - return hr; -} - -extern "C" HRESULT DAPI FreeCryptProvFromCert( - __in BOOL fAcquired, - __in HCRYPTPROV hProv, - __in_opt LPWSTR pwszCapiProvider, - __in DWORD dwProviderType, - __in_opt LPWSTR pwszTmpContainer - ) -{ - HRESULT hr = S_OK; - HMODULE hMsSign32 = NULL; - - typedef void (WINAPI *FREECRYPTPROVFROMCERT)(BOOL, HCRYPTPROV, LPWSTR, DWORD, LPWSTR); - FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL; - - hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); - CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); - - pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert"); - CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); - - pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer); -LExit: - return hr; -} - -extern "C" HRESULT DAPI GetProvSecurityDesc( - __in HCRYPTPROV hProv, - __deref_out SECURITY_DESCRIPTOR** ppSecurity) -{ - HRESULT hr = S_OK; - ULONG ulSize = 0; - SECURITY_DESCRIPTOR* pSecurity = NULL; - - // Get the size of the security descriptor. - if (!::CryptGetProvParam( - hProv, - PP_KEYSET_SEC_DESCR, - NULL, - &ulSize, - DACL_SECURITY_INFORMATION)) - { - CertExitWithLastError(hr, "Error getting security descriptor size for CSP."); - } - - // Allocate the memory for the security descriptor. - pSecurity = static_cast(MemAlloc(ulSize, TRUE)); - CertExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL"); - - // Get the security descriptor. - if (!::CryptGetProvParam( - hProv, - PP_KEYSET_SEC_DESCR, - (BYTE*)pSecurity, - &ulSize, - DACL_SECURITY_INFORMATION)) - { - MemFree(pSecurity); - CertExitWithLastError(hr, "Error getting security descriptor for CSP."); - } - *ppSecurity = pSecurity; - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI SetProvSecurityDesc( - __in HCRYPTPROV hProv, - __in SECURITY_DESCRIPTOR* pSecurity) -{ - HRESULT hr = S_OK; - - // Set the new security descriptor. - if (!::CryptSetProvParam( - hProv, - PP_KEYSET_SEC_DESCR, - (BYTE*)pSecurity, - DACL_SECURITY_INFORMATION)) - { - CertExitWithLastError(hr, "Error setting security descriptor for CSP."); - } -LExit: - return hr; -} - -extern "C" BOOL DAPI CertHasPrivateKey( - __in PCCERT_CONTEXT pCertContext, - __out_opt DWORD* pdwKeySpec) -{ - HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey = NULL; - DWORD dwKeySpec = 0; - // set CRYPT_ACQUIRE_CACHE_FLAG so that we don't have to release the private key handle - BOOL fResult = ::CryptAcquireCertificatePrivateKey( - pCertContext, - CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG, - 0, //pvReserved - &hPrivateKey, - &dwKeySpec, - NULL - ); - if (pdwKeySpec) - { - *pdwKeySpec = dwKeySpec; - } - return fResult; -} - - -extern "C" HRESULT DAPI CertInstallSingleCertificate( - __in HCERTSTORE hStore, - __in PCCERT_CONTEXT pCertContext, - __in LPCWSTR wzName - ) -{ - HRESULT hr = S_OK; - CERT_BLOB blob = { }; - - DWORD dwKeySpec = 0; - - HCRYPTPROV hCsp = NULL; - LPWSTR pwszTmpContainer = NULL; - LPWSTR pwszProviderName = NULL; - DWORD dwProviderType = 0; - BOOL fAcquired = TRUE; - - SECURITY_DESCRIPTOR* pSecurity = NULL; - SECURITY_DESCRIPTOR* pSecurityNew = NULL; - - // Update the friendly name of the certificate to be configured. - blob.pbData = (BYTE*)wzName; - blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null - - if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob)) - { - CertExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName); - } - - if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) - { - CertExitWithLastError(hr, "Failed to add certificate to the store."); - } - - // if the certificate has a private key, grant Administrators access - if (CertHasPrivateKey(pCertContext, &dwKeySpec)) - { - if (AT_KEYEXCHANGE == dwKeySpec || AT_SIGNATURE == dwKeySpec) - { - // We added a CSP key - hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType); - CertExitOnFailure(hr, "Failed to get handle to CSP"); - - hr = GetProvSecurityDesc(hCsp, &pSecurity); - CertExitOnFailure(hr, "Failed to get security descriptor of CSP"); - - hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew); - CertExitOnFailure(hr, "Failed to create new security descriptor"); - - hr = SetProvSecurityDesc(hCsp, pSecurityNew); - CertExitOnFailure(hr, "Failed to set Admin ACL on CSP"); - } - - if (CERT_NCRYPT_KEY_SPEC == dwKeySpec) - { - // We added a CNG key - // TODO change ACL on CNG key - } - } -LExit: - if (hCsp) - { - FreeCryptProvFromCert(fAcquired, hCsp, NULL, dwProviderType, NULL); - } - - ReleaseMem(pSecurity); - - if (pSecurityNew) - { - AclFreeSecurityDescriptor(pSecurityNew); - } - return hr; -} diff --git a/src/dutil/conutil.cpp b/src/dutil/conutil.cpp deleted file mode 100644 index 33e1b59a..00000000 --- a/src/dutil/conutil.cpp +++ /dev/null @@ -1,673 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) -#define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) -#define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) -#define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) -#define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__) -#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__) - - -static HANDLE vhStdIn = INVALID_HANDLE_VALUE; -static HANDLE vhStdOut = INVALID_HANDLE_VALUE; -static BOOL vfConsoleIn = FALSE; -static BOOL vfConsoleOut = FALSE; -static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; - - -extern "C" HRESULT DAPI ConsoleInitialize() -{ - Assert(INVALID_HANDLE_VALUE == vhStdOut); - HRESULT hr = S_OK; - UINT er; - - vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); - if (INVALID_HANDLE_VALUE == vhStdIn) - { - ConExitOnLastError(hr, "failed to open stdin"); - } - - vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); - if (INVALID_HANDLE_VALUE == vhStdOut) - { - ConExitOnLastError(hr, "failed to open stdout"); - } - - // check if we have a std in on the console - if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo)) - { - vfConsoleIn = TRUE; - } - else - { - er = ::GetLastError(); - if (ERROR_INVALID_HANDLE == er) - { - vfConsoleIn= FALSE; - hr = S_OK; - } - else - { - ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); - } - } - - if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo)) - { - vfConsoleOut = TRUE; - } - else // no console - { - memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); - er = ::GetLastError(); - if (ERROR_INVALID_HANDLE == er) - { - vfConsoleOut = FALSE; - hr = S_OK; - } - else - { - ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); - } - } - -LExit: - if (FAILED(hr)) - { - if (INVALID_HANDLE_VALUE != vhStdOut) - { - ::CloseHandle(vhStdOut); - } - - if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) - { - ::CloseHandle(vhStdIn); - } - - vhStdOut = INVALID_HANDLE_VALUE; - vhStdIn = INVALID_HANDLE_VALUE; - } - - return hr; -} - - -extern "C" void DAPI ConsoleUninitialize() -{ - BOOL fOutEqualsIn = vhStdOut == vhStdIn; - - memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); - - if (INVALID_HANDLE_VALUE != vhStdOut) - { - ::CloseHandle(vhStdOut); - } - - if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn) - { - ::CloseHandle(vhStdIn); - } - - vhStdOut = INVALID_HANDLE_VALUE; - vhStdIn = INVALID_HANDLE_VALUE; -} - - -extern "C" void DAPI ConsoleGreen() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY); - } -} - - -extern "C" void DAPI ConsoleRed() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY); - } -} - - -extern "C" void DAPI ConsoleYellow() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); - } -} - - -extern "C" void DAPI ConsoleNormal() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes); - } -} - - -/******************************************************************** - ConsoleWrite - full color printfA without libc - - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") - assumes already in normal color and resets the screen to normal color -********************************************************************/ -extern "C" HRESULT DAPI ConsoleWrite( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - LPSTR pszOutput = NULL; - DWORD cchOutput = 0; - DWORD cbWrote = 0; - DWORD cbTotal = 0; - - // set the color - switch (cc) - { - case CONSOLE_COLOR_NORMAL: break; // do nothing - case CONSOLE_COLOR_RED: ConsoleRed(); break; - case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; - case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; - } - - va_list args; - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); - va_end(args); - ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); - - cchOutput = lstrlenA(pszOutput); - while (cbTotal < (sizeof(*pszOutput) * cchOutput)) - { - if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) - { - ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); - } - - cbTotal += cbWrote; - } - - // reset the color to normal - if (CONSOLE_COLOR_NORMAL != cc) - { - ConsoleNormal(); - } - -LExit: - ReleaseStr(pszOutput); - return hr; -} - - -/******************************************************************** - ConsoleWriteLine - full color printfA plus newline without libc - - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") - assumes already in normal color and resets the screen to normal color -********************************************************************/ -extern "C" HRESULT DAPI ConsoleWriteLine( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - LPSTR pszOutput = NULL; - DWORD cchOutput = 0; - DWORD cbWrote = 0; - DWORD cbTotal = 0; - LPCSTR szNewLine = "\r\n"; - - // set the color - switch (cc) - { - case CONSOLE_COLOR_NORMAL: break; // do nothing - case CONSOLE_COLOR_RED: ConsoleRed(); break; - case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; - case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; - } - - va_list args; - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); - va_end(args); - ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); - - // - // write the string - // - cchOutput = lstrlenA(pszOutput); - while (cbTotal < (sizeof(*pszOutput) * cchOutput)) - { - if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) - ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); - - cbTotal += cbWrote; - } - - // - // write the newline - // - if (!::WriteFile(vhStdOut, reinterpret_cast(szNewLine), 2, &cbWrote, NULL)) - { - ConExitOnLastError(hr, "failed to write newline to console"); - } - - // reset the color to normal - if (CONSOLE_COLOR_NORMAL != cc) - { - ConsoleNormal(); - } - -LExit: - ReleaseStr(pszOutput); - return hr; -} - - -/******************************************************************** - ConsoleWriteError - display an error to the screen - - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d") -********************************************************************/ -HRESULT ConsoleWriteError( - HRESULT hrError, - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - LPSTR pszMessage = NULL; - - va_list args; - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args); - va_end(args); - ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); - - if (FAILED(hrError)) - { - hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage); - } - else - { - hr = ConsoleWriteLine(cc, "Error: %s", pszMessage); - } - -LExit: - ReleaseStr(pszMessage); - return hr; -} - - -/******************************************************************** - ConsoleReadW - get console input without libc - - NOTE: only supports reading ANSI characters -********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadW( - __deref_out_z LPWSTR* ppwzBuffer - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - Assert(ppwzBuffer); - - HRESULT hr = S_OK; - LPSTR psz = NULL; - DWORD cch = 0; - DWORD cchRead = 0; - DWORD cchTotalRead = 0; - - cch = 64; - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - - // loop until we read the \r\n from the console - for (;;) - { - // read one character at a time, since that seems to be the only way to make this work - if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) - ConExitOnLastError(hr, "failed to read string from console"); - - cchTotalRead += cchRead; - if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1]) - { - psz[cchTotalRead - 2] = '\0'; // chop off the \r\n - break; - } - else if (0 == cchRead) // nothing more was read - { - psz[cchTotalRead] = '\0'; // null termintate and bail - break; - } - - if (cchTotalRead == cch) - { - cch *= 2; // double everytime we run out of space - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - } - } - - hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP); - -LExit: - ReleaseStr(psz); - return hr; -} - - -/******************************************************************** - ConsoleReadNonBlockingW - Read from the console without blocking - Won't work for redirected files (exe < txtfile), but will work for stdin redirected to - an anonymous or named pipe - - if (fReadLine), stop reading immediately when \r\n is found -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadNonBlockingW( - __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, - __out DWORD* pcchSize, - BOOL fReadLine - ) -{ - Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize); - HRESULT hr = S_OK; - - LPSTR psz = NULL; - - ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); - - DWORD dwRead; - DWORD dwNumInput; - - DWORD cchTotal = 0; - DWORD cch = 8; - - DWORD cchRead = 0; - DWORD cchTotalRead = 0; - - DWORD dwIndex = 0; - DWORD er; - - INPUT_RECORD ir; - WCHAR chIn; - - *ppwzBuffer = NULL; - *pcchSize = 0; - - // If we really have a handle to stdin, and not the end of a pipe - if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL)) - { - er = ::GetLastError(); - if (ERROR_INVALID_HANDLE != er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - - if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead)) - { - ConExitOnLastError(hr, "failed to peek at console input"); - } - - if (0 == dwRead) - { - ExitFunction1(hr = S_FALSE); - } - - for (/* dwRead from num of input events */; dwRead > 0; dwRead--) - { - if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput)) - { - ConExitOnLastError(hr, "Failed to read input from console"); - } - - // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested - if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown) - { - chIn = ir.Event.KeyEvent.uChar.UnicodeChar; - - if (0 == cchTotal) - { - cchTotal = cch; - cch *= 2; - StrAlloc(ppwzBuffer, cch); - } - - (*ppwzBuffer)[dwIndex] = chIn; - - if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex])) - { - *ppwzBuffer[dwIndex - 1] = L'\0'; - dwIndex -= 1; - break; - } - - ++dwIndex; - cchTotal--; - } - } - - *pcchSize = dwIndex; - } - else - { - // otherwise, the peek worked, and we have the end of a pipe - if (0 == dwRead) - ExitFunction1(hr = S_FALSE); - - cch = 8; - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - - for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--) - { - // read one character at a time, since that seems to be the only way to make this work - if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - - cchTotalRead += cchRead; - if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead]) - { - psz[cchTotalRead - 1] = '\0'; // chop off the \r\n - cchTotalRead -= 1; - break; - } - else if (0 == cchRead) // nothing more was read - { - psz[cchTotalRead] = '\0'; // null termintate and bail - break; - } - - if (cchTotalRead == cch) - { - cch *= 2; // double everytime we run out of space - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - } - } - - *pcchSize = cchTotalRead; - hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP); - } - -LExit: - ReleaseStr(psz); - - return hr; -} - - -/******************************************************************** - ConsoleReadStringA - get console input without libc - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadStringA( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, - CONST DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) - { - DWORD iRead = 1; - DWORD iReadCharTotal = 0; - if (ppszCharBuffer && *ppszCharBuffer == NULL) - { - do - { - hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); - ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); - // ReadConsoleW will not return until , the last two chars are 13 and 10. - if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - iReadCharTotal += *pcchNumCharReturn; - iRead += 1; - } - while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13); - *pcchNumCharReturn = iReadCharTotal; - } - else - { - if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || - *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || - (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) - { - // need read more - hr = ERROR_MORE_DATA; - } - } - } - else - { - hr = E_INVALIDARG; - } - -LExit: - return hr; -} - -/******************************************************************** - ConsoleReadStringW - get console input without libc - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadStringW( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, - const DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) - { - DWORD iRead = 1; - DWORD iReadCharTotal = 0; - if (*ppwzCharBuffer == NULL) - { - do - { - hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); - ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); - // ReadConsoleW will not return until , the last two chars are 13 and 10. - if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - iReadCharTotal += *pcchNumCharReturn; - iRead += 1; - } - while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13); - *pcchNumCharReturn = iReadCharTotal; - } - else - { - if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || - *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || - (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) - { - // need read more - hr = ERROR_MORE_DATA; - } - } - } - else - { - hr = E_INVALIDARG; - } - -LExit: - return hr; -} - -/******************************************************************** - ConsoleSetReadHidden - set console input no echo - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleSetReadHidden(void) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - ::FlushConsoleInputBuffer(vhStdIn); - if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) - { - ConExitOnLastError(hr, "failed to set console input mode to be hidden"); - } - -LExit: - return hr; -} - -/******************************************************************** - ConsoleSetReadNormal - reset to echo - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleSetReadNormal(void) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) - { - ConExitOnLastError(hr, "failed to set console input mode to be normal"); - } - -LExit: - return hr; -} - diff --git a/src/dutil/cryputil.cpp b/src/dutil/cryputil.cpp deleted file mode 100644 index 24bb83f1..00000000 --- a/src/dutil/cryputil.cpp +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CrypExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) -#define CrypExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) -#define CrypExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) -#define CrypExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) -#define CrypExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CRYPUTIL, e, x, s, __VA_ARGS__) -#define CrypExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CRYPUTIL, g, x, s, __VA_ARGS__) - -static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL; -static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL; -static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL; -static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL; - -static HMODULE vhAdvApi32Dll = NULL; -static HMODULE vhCrypt32Dll = NULL; -static BOOL vfCrypInitialized = FALSE; - -// function definitions - -/******************************************************************** - CrypInitialize - initializes cryputil - -*********************************************************************/ -extern "C" HRESULT DAPI CrypInitialize( - ) -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); - if (SUCCEEDED(hr)) - { - // Ignore failures - if these don't exist, we'll try the Crypt methods. - vpfnRtlEncryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction040")); - vpfnRtlDecryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction041")); - } - if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory) - { - hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll); - CrypExitOnFailure(hr, "Failed to load Crypt32.dll"); - - vpfnCryptProtectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory")); - if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory) - { - CrypExitWithLastError(hr, "Failed to load an encryption method"); - } - vpfnCryptUnprotectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory")); - if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory) - { - CrypExitWithLastError(hr, "Failed to load a decryption method"); - } - } - - vfCrypInitialized = TRUE; - -LExit: - return hr; -} - - -/******************************************************************** - CrypUninitialize - uninitializes cryputil - -*********************************************************************/ -extern "C" void DAPI CrypUninitialize( - ) -{ - if (vhAdvApi32Dll) - { - ::FreeLibrary(vhAdvApi32Dll); - vhAdvApi32Dll = NULL; - vpfnRtlEncryptMemory = NULL; - vpfnRtlDecryptMemory = NULL; - } - - if (vhCrypt32Dll) - { - ::FreeLibrary(vhCrypt32Dll); - vhCrypt32Dll = NULL; - vpfnCryptProtectMemory = NULL; - vpfnCryptUnprotectMemory = NULL; - } - - vfCrypInitialized = FALSE; -} - -extern "C" HRESULT DAPI CrypDecodeObject( - __in_z LPCSTR szStructType, - __in_ecount(cbData) const BYTE* pbData, - __in DWORD cbData, - __in DWORD dwFlags, - __out LPVOID* ppvObject, - __out_opt DWORD* pcbObject - ) -{ - HRESULT hr = S_OK; - LPVOID pvObject = NULL; - DWORD cbObject = 0; - - if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject)) - { - CrypExitWithLastError(hr, "Failed to decode object to determine size."); - } - - pvObject = MemAlloc(cbObject, TRUE); - CrypExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object."); - - if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject)) - { - CrypExitWithLastError(hr, "Failed to decode object."); - } - - *ppvObject = pvObject; - pvObject = NULL; - - if (pcbObject) - { - *pcbObject = cbObject; - } - -LExit: - ReleaseMem(pvObject); - - return hr; -} - - -extern "C" HRESULT DAPI CrypMsgGetParam( - __in HCRYPTMSG hCryptMsg, - __in DWORD dwType, - __in DWORD dwIndex, - __out LPVOID* ppvData, - __out_opt DWORD* pcbData - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - DWORD cb = 0; - - if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb)) - { - CrypExitWithLastError(hr, "Failed to get crypt message parameter data size."); - } - - pv = MemAlloc(cb, TRUE); - CrypExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter."); - - if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb)) - { - CrypExitWithLastError(hr, "Failed to get crypt message parameter."); - } - - *ppvData = pv; - pv = NULL; - - if (pcbData) - { - *pcbData = cb; - } - -LExit: - ReleaseMem(pv); - - return hr; -} - - -extern "C" HRESULT DAPI CrypHashFile( - __in_z LPCWSTR wzFilePath, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // open input file - hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - CrypExitWithLastError(hr, "Failed to open input file."); - } - - hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed); - CrypExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath); - -LExit: - ReleaseFileHandle(hFile); - - return hr; -} - - -extern "C" HRESULT DAPI CrypHashFileHandle( - __in HANDLE hFile, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ) -{ - HRESULT hr = S_OK; - HCRYPTPROV hProv = NULL; - HCRYPTHASH hHash = NULL; - DWORD cbRead = 0; - BYTE rgbBuffer[4096] = { }; - const LARGE_INTEGER liZero = { }; - - // get handle to the crypto provider - if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - { - CrypExitWithLastError(hr, "Failed to acquire crypto context."); - } - - // initiate hash - if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) - { - CrypExitWithLastError(hr, "Failed to initiate hash."); - } - - for (;;) - { - // read data block - if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL)) - { - CrypExitWithLastError(hr, "Failed to read data block."); - } - - if (!cbRead) - { - break; // end of file - } - - // hash data block - if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0)) - { - CrypExitWithLastError(hr, "Failed to hash data block."); - } - } - - // get hash value - if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) - { - CrypExitWithLastError(hr, "Failed to get hash value."); - } - - if (pqwBytesHashed) - { - if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT)) - { - CrypExitWithLastError(hr, "Failed to get file pointer."); - } - } - -LExit: - if (hHash) - { - ::CryptDestroyHash(hHash); - } - if (hProv) - { - ::CryptReleaseContext(hProv, 0); - } - - return hr; -} - -HRESULT DAPI CrypHashBuffer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash - ) -{ - HRESULT hr = S_OK; - HCRYPTPROV hProv = NULL; - HCRYPTHASH hHash = NULL; - DWORD cbDataHashed = 0; - SIZE_T cbTotal = 0; - SIZE_T cbRemaining = 0; - - // get handle to the crypto provider - if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - { - CrypExitWithLastError(hr, "Failed to acquire crypto context."); - } - - // initiate hash - if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) - { - CrypExitWithLastError(hr, "Failed to initiate hash."); - } - - do - { - cbRemaining = cbBuffer - cbTotal; - cbDataHashed = (DWORD)min(DWORD_MAX, cbRemaining); - if (!::CryptHashData(hHash, pbBuffer + cbTotal, cbDataHashed, 0)) - { - CrypExitWithLastError(hr, "Failed to hash data."); - } - - cbTotal += cbDataHashed; - } while (cbTotal < cbBuffer); - - // get hash value - if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) - { - CrypExitWithLastError(hr, "Failed to get hash value."); - } - -LExit: - if (hHash) - { - ::CryptDestroyHash(hHash); - } - if (hProv) - { - ::CryptReleaseContext(hProv, 0); - } - - return hr; -} - -HRESULT DAPI CrypEncryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ) -{ - HRESULT hr = E_FAIL; - - if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) - { - hr = E_INVALIDARG; - } - else if (vpfnRtlEncryptMemory) - { - hr = static_cast(vpfnRtlEncryptMemory(pData, cbData, dwFlags)); - } - else if (vpfnCryptProtectMemory) - { - if (vpfnCryptProtectMemory(pData, cbData, dwFlags)) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - } - } - CrypExitOnFailure(hr, "Failed to encrypt memory"); -LExit: - return hr; -} - -HRESULT DAPI CrypDecryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ) -{ - HRESULT hr = E_FAIL; - - if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) - { - hr = E_INVALIDARG; - } - else if (vpfnRtlDecryptMemory) - { - hr = static_cast(vpfnRtlDecryptMemory(pData, cbData, dwFlags)); - } - else if (vpfnCryptUnprotectMemory) - { - if (vpfnCryptUnprotectMemory(pData, cbData, dwFlags)) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - } - } - CrypExitOnFailure(hr, "Failed to decrypt memory"); -LExit: - return hr; -} - diff --git a/src/dutil/deputil.cpp b/src/dutil/deputil.cpp deleted file mode 100644 index 2e6d6a6c..00000000 --- a/src/dutil/deputil.cpp +++ /dev/null @@ -1,699 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DepExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) -#define DepExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) -#define DepExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) -#define DepExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) -#define DepExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEPUTIL, e, x, s, __VA_ARGS__) -#define DepExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEPUTIL, g, x, s, __VA_ARGS__) - -#define ARRAY_GROWTH_SIZE 5 - -static LPCWSTR vcszVersionValue = L"Version"; -static LPCWSTR vcszDisplayNameValue = L"DisplayName"; -static LPCWSTR vcszMinVersionValue = L"MinVersion"; -static LPCWSTR vcszMaxVersionValue = L"MaxVersion"; -static LPCWSTR vcszAttributesValue = L"Attributes"; - -enum eRequiresAttributes { RequiresAttributesMinVersionInclusive = 256, RequiresAttributesMaxVersionInclusive = 512 }; - -// We write to Software\Classes explicitly based on the current security context instead of HKCR. -// See http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. -static LPCWSTR vsczRegistryRoot = L"Software\\Classes\\Installer\\Dependencies\\"; -static LPCWSTR vsczRegistryDependents = L"Dependents"; - -static HRESULT AllocDependencyKeyName( - __in_z LPCWSTR wzName, - __deref_out_z LPWSTR* psczKeyName - ); - -static HRESULT GetDependencyNameFromKey( - __in HKEY hkHive, - __in LPCWSTR wzKey, - __deref_out_z LPWSTR* psczName - ); - -DAPI_(HRESULT) DepGetProviderInformation( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __deref_out_z_opt LPWSTR* psczId, - __deref_out_z_opt LPWSTR* psczName, - __deref_out_z_opt LPWSTR* psczVersion - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the dependency key. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); - - // Get the Id if requested and available. - if (psczId) - { - hr = RegReadString(hkKey, NULL, psczId); - if (E_FILENOTFOUND == hr) - { - hr = S_OK; - } - DepExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey); - } - - // Get the DisplayName if requested and available. - if (psczName) - { - hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); - if (E_FILENOTFOUND == hr) - { - hr = S_OK; - } - DepExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey); - } - - // Get the Version if requested and available. - if (psczVersion) - { - hr = RegReadString(hkKey, vcszVersionValue, psczVersion); - if (E_FILENOTFOUND == hr) - { - hr = S_OK; - } - DepExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepCheckDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes, - __in STRINGDICT_HANDLE sdDependencies, - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - DWORD64 dw64Version = 0; - DWORD64 dw64MinVersion = 0; - DWORD64 dw64MaxVersion = 0; - BOOL fAllowEqual = FALSE; - LPWSTR sczName = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the key. If that fails, add the missing dependency key to the dependency array if it doesn't already exist. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); - - // If there are no registry values, consider the key orphaned and treat it as missing. - hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); - } - } - - // If the key was not found or the Version value was not found, add the missing dependency key to the dependency array. - if (E_FILENOTFOUND == hr) - { - hr = DictKeyExists(sdDependencies, wzProviderKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey); - } - else - { - hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, NULL); - DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey); - - hr = DictAddKey(sdDependencies, wzProviderKey); - DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey); - } - - // Exit since the check already failed. - ExitFunction1(hr = E_NOTFOUND); - } - - // Check MinVersion if provided. - if (wzMinVersion) - { - if (*wzMinVersion) - { - hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion); - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); - - fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; - if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) - { - hr = DictKeyExists(sdDependencies, wzProviderKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey); - } - else - { - hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); - DepExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey); - - hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); - DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey); - - hr = DictAddKey(sdDependencies, wzProviderKey); - DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey); - } - - // Exit since the check already failed. - ExitFunction1(hr = E_NOTFOUND); - } - } - } - - // Check MaxVersion if provided. - if (wzMaxVersion) - { - if (*wzMaxVersion) - { - hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion); - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); - - fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; - if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) - { - hr = DictKeyExists(sdDependencies, wzProviderKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey); - } - else - { - hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); - DepExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey); - - hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); - DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey); - - hr = DictAddKey(sdDependencies, wzProviderKey); - DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey); - } - - // Exit since the check already failed. - ExitFunction1(hr = E_NOTFOUND); - } - } - } - -LExit: - ReleaseStr(sczName); - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepCheckDependents( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __reserved int /*iAttributes*/, - __in C_STRINGDICT_HANDLE sdIgnoredDependents, - __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, - __inout LPUINT pcDependents - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkProviderKey = NULL; - HKEY hkDependentsKey = NULL; - LPWSTR sczDependentKey = NULL; - LPWSTR sczDependentName = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the key. If that fails, the dependency information is corrupt. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkProviderKey); - DepExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey); - - // Try to open the dependencies key. If that does not exist, there are no dependents. - hr = RegOpen(hkProviderKey, vsczRegistryDependents, KEY_READ, &hkDependentsKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey); - } - else - { - ExitFunction1(hr = S_OK); - } - - // Now enumerate the dependent keys. If they are not defined in the ignored list, add them to the array. - for (DWORD dwIndex = 0; ; ++dwIndex) - { - hr = RegKeyEnum(hkDependentsKey, dwIndex, &sczDependentKey); - if (E_NOMOREITEMS != hr) - { - DepExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey); - } - else - { - hr = S_OK; - break; - } - - // If the key isn't ignored, add it to the dependent array. - hr = DictKeyExists(sdIgnoredDependents, sczDependentKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); - } - else - { - // Get the name of the dependent from the key. - hr = GetDependencyNameFromKey(hkHive, sczDependentKey, &sczDependentName); - DepExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey); - - hr = DepDependencyArrayAlloc(prgDependents, pcDependents, sczDependentKey, sczDependentName); - DepExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey); - } - } - -LExit: - ReleaseStr(sczDependentName); - ReleaseStr(sczDependentKey); - ReleaseRegKey(hkDependentsKey); - ReleaseRegKey(hkProviderKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepRegisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z LPCWSTR wzVersion, - __in_z LPCWSTR wzDisplayName, - __in_z_opt LPCWSTR wzId, - __in int iAttributes - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - BOOL fCreated = FALSE; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Create the dependency key (or open it if it already exists). - hr = RegCreateEx(hkHive, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); - DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey); - - // Set the id if it was provided. - if (wzId) - { - hr = RegWriteString(hkKey, NULL, wzId); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId); - } - - // Set the version. - hr = RegWriteString(hkKey, vcszVersionValue, wzVersion); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion); - - // Set the display name. - hr = RegWriteString(hkKey, vcszDisplayNameValue, wzDisplayName); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName); - - // Set the attributes if non-zero. - if (0 != iAttributes) - { - hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); - DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepDependentExists( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDependentKey = NULL; - HKEY hkDependentKey = NULL; - - // Format the provider dependents registry key. - hr = StrAllocFormatted(&sczDependentKey, L"%ls%ls\\%ls\\%ls", vsczRegistryRoot, wzDependencyProviderKey, vsczRegistryDependents, wzProviderKey); - DepExitOnFailure(hr, "Failed to format registry key to dependent."); - - hr = RegOpen(hkHive, sczDependentKey, KEY_READ, &hkDependentKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey); - } - -LExit: - ReleaseRegKey(hkDependentKey); - ReleaseStr(sczDependentKey); - - return hr; -} - -DAPI_(HRESULT) DepRegisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDependencyKey = NULL; - HKEY hkDependencyKey = NULL; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - BOOL fCreated = FALSE; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzDependencyProviderKey, &sczDependencyKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey); - - // Create the dependency key (or open it if it already exists). - hr = RegCreateEx(hkHive, sczDependencyKey, KEY_WRITE, FALSE, NULL, &hkDependencyKey, &fCreated); - DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey); - - // Create the subkey to register the dependent. - hr = StrAllocFormatted(&sczKey, L"%ls\\%ls", vsczRegistryDependents, wzProviderKey); - DepExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); - - hr = RegCreateEx(hkDependencyKey, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); - DepExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey); - - // Set the minimum version if not NULL. - hr = RegWriteString(hkKey, vcszMinVersionValue, wzMinVersion); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion); - - // Set the maximum version if not NULL. - hr = RegWriteString(hkKey, vcszMaxVersionValue, wzMaxVersion); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion); - - // Set the attributes if non-zero. - if (0 != iAttributes) - { - hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); - DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - ReleaseRegKey(hkDependencyKey); - ReleaseStr(sczDependencyKey); - - return hr; -} - -DAPI_(HRESULT) DepUnregisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Delete the entire key including all sub-keys. - hr = RegDelete(hkHive, sczKey, REG_KEY_DEFAULT, TRUE); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepUnregisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ) -{ - HRESULT hr = S_OK; - HKEY hkRegistryRoot = NULL; - HKEY hkDependencyProviderKey = NULL; - HKEY hkRegistryDependents = NULL; - DWORD cSubKeys = 0; - DWORD cValues = 0; - - // Open the root key. We may delete the wzDependencyProviderKey during clean up. - hr = RegOpen(hkHive, vsczRegistryRoot, KEY_READ, &hkRegistryRoot); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot); - } - else - { - ExitFunction(); - } - - // Try to open the dependency key. If that does not exist, simply return. - hr = RegOpen(hkRegistryRoot, wzDependencyProviderKey, KEY_READ, &hkDependencyProviderKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey); - } - else - { - ExitFunction(); - } - - // Try to open the dependents subkey to enumerate. - hr = RegOpen(hkDependencyProviderKey, vsczRegistryDependents, KEY_READ, &hkRegistryDependents); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); - } - else - { - ExitFunction(); - } - - // Delete the wzProviderKey dependent sub-key. - hr = RegDelete(hkRegistryDependents, wzProviderKey, REG_KEY_DEFAULT, TRUE); - DepExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); - - // If there are no remaining dependents, delete the Dependents subkey. - hr = RegQueryKey(hkRegistryDependents, &cSubKeys, NULL); - DepExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey); - - if (0 < cSubKeys) - { - ExitFunction(); - } - - // Release the handle to make sure it's deleted immediately. - ReleaseRegKey(hkRegistryDependents); - - // Fail if there are any subkeys since we just checked. - hr = RegDelete(hkDependencyProviderKey, vsczRegistryDependents, REG_KEY_DEFAULT, FALSE); - DepExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); - - // If there are no values, delete the provider dependency key. - hr = RegQueryKey(hkDependencyProviderKey, NULL, &cValues); - DepExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey); - - if (0 == cValues) - { - // Release the handle to make sure it's deleted immediately. - ReleaseRegKey(hkDependencyProviderKey); - - // Fail if there are any subkeys since we just checked. - hr = RegDelete(hkRegistryRoot, wzDependencyProviderKey, REG_KEY_DEFAULT, FALSE); - DepExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey); - } - -LExit: - ReleaseRegKey(hkRegistryDependents); - ReleaseRegKey(hkDependencyProviderKey); - ReleaseRegKey(hkRegistryRoot); - - return hr; -} - -DAPI_(HRESULT) DepDependencyArrayAlloc( - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies, - __in_z LPCWSTR wzKey, - __in_z_opt LPCWSTR wzName - ) -{ - HRESULT hr = S_OK; - UINT cRequired = 0; - DEPENDENCY* pDependency = NULL; - - hr = ::UIntAdd(*pcDependencies, 1, &cRequired); - DepExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array."); - - hr = MemEnsureArraySize(reinterpret_cast(prgDependencies), cRequired, sizeof(DEPENDENCY), ARRAY_GROWTH_SIZE); - DepExitOnFailure(hr, "Failed to allocate memory for the dependency array."); - - pDependency = static_cast(&(*prgDependencies)[*pcDependencies]); - DepExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid."); - - hr = StrAllocString(&(pDependency->sczKey), wzKey, 0); - DepExitOnFailure(hr, "Failed to allocate the string key in the dependency array."); - - if (wzName) - { - hr = StrAllocString(&(pDependency->sczName), wzName, 0); - DepExitOnFailure(hr, "Failed to allocate the string name in the dependency array."); - } - - // Update the number of current elements in the dependency array. - *pcDependencies = cRequired; - -LExit: - return hr; -} - -DAPI_(void) DepDependencyArrayFree( - __in_ecount(cDependencies) DEPENDENCY* rgDependencies, - __in UINT cDependencies - ) -{ - for (UINT i = 0; i < cDependencies; ++i) - { - ReleaseStr(rgDependencies[i].sczKey); - ReleaseStr(rgDependencies[i].sczName); - } - - ReleaseMem(rgDependencies); -} - -/*************************************************************************** - AllocDependencyKeyName - Allocates and formats the root registry key name. - -***************************************************************************/ -static HRESULT AllocDependencyKeyName( - __in_z LPCWSTR wzName, - __deref_out_z LPWSTR* psczKeyName - ) -{ - HRESULT hr = S_OK; - size_t cchName = 0; - size_t cchKeyName = 0; - - // Get the length of the static registry root once. - static size_t cchRegistryRoot = ::lstrlenW(vsczRegistryRoot); - - // Get the length of the dependency, and add to the length of the root. - hr = ::StringCchLengthW(wzName, STRSAFE_MAX_CCH, &cchName); - DepExitOnFailure(hr, "Failed to get string length of dependency name."); - - // Add the sizes together to allocate memory once (callee will add space for nul). - hr = ::SizeTAdd(cchRegistryRoot, cchName, &cchKeyName); - DepExitOnFailure(hr, "Failed to add the string lengths together."); - - // Allocate and concat the strings together. - hr = StrAllocString(psczKeyName, vsczRegistryRoot, cchKeyName); - DepExitOnFailure(hr, "Failed to allocate string for dependency registry root."); - - hr = StrAllocConcat(psczKeyName, wzName, cchName); - DepExitOnFailure(hr, "Failed to concatenate the dependency key name."); - -LExit: - return hr; -} - -/*************************************************************************** - GetDependencyNameFromKey - Attempts to name of the dependency from the key. - -***************************************************************************/ -static HRESULT GetDependencyNameFromKey( - __in HKEY hkHive, - __in LPCWSTR wzProviderKey, - __deref_out_z LPWSTR* psczName - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the dependency key. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); - } - else - { - ExitFunction1(hr = S_OK); - } - - // Get the DisplayName if available. - hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey); - } - else - { - ExitFunction1(hr = S_OK); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} diff --git a/src/dutil/dictutil.cpp b/src/dutil/dictutil.cpp deleted file mode 100644 index 0d0743eb..00000000 --- a/src/dutil/dictutil.cpp +++ /dev/null @@ -1,784 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DictExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) -#define DictExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) -#define DictExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) -#define DictExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) -#define DictExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DICTUTIL, e, x, s, __VA_ARGS__) -#define DictExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DICTUTIL, g, x, s, __VA_ARGS__) - -// These should all be primes, and spaced reasonably apart (currently each is about 4x the last) -const DWORD MAX_BUCKET_SIZES[] = { - 503, - 2017, - 7937, - 32779, - 131111, - 524341, - 2097709, - 8390857, - 33563437, - 134253719, - 537014927, - 2148059509 - }; - -// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions -#define MAX_BUCKETS_TO_ITEMS_RATIO 8 - -enum DICT_TYPE -{ - DICT_INVALID = 0, - DICT_EMBEDDED_KEY = 1, - DICT_STRING_LIST = 2 -}; - -struct STRINGDICT_STRUCT -{ - DICT_TYPE dtType; - - // Optional flags to control the behavior of the dictionary. - DICT_FLAG dfFlags; - - // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated - DWORD dwBucketSizeIndex; - - // Number of items currently stored in the dict buckets - DWORD dwNumItems; - - // Byte offset of key within bucket value, for collision checking - see - // comments above DictCreateEmbeddedKey() implementation for further details - size_t cByteOffset; - - // The actual stored buckets - void **ppvBuckets; - - // The actual stored items in the order they were added (used for auto freeing or enumerating) - void **ppvItemList; - - // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm - void **ppvValueArray; -}; - -const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT); - -static HRESULT StringHash( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwNumBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwHash - ); -static BOOL IsMatchExact( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwMatchIndex, - __in_z LPCWSTR wzOriginalString - ); -static HRESULT GetValue( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out_opt void **ppvValue - ); -static HRESULT GetInsertIndex( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwBucketCount, - __in void **ppvBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ); -static HRESULT GetIndex( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ); -static LPCWSTR GetKey( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ); -static HRESULT GrowDictionary( - __inout STRINGDICT_STRUCT *psd - ); -// These 2 helper functions allow us to safely handle dictutil consumers resizing -// the value array by storing "offsets" instead of raw void *'s in our buckets. -static void * TranslateOffsetToValue( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ); -static void * TranslateValueToOffset( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ); - -// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s). -// However, to support collision checking, the key needs to be represented in the "value" object (pointed to -// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer -// within the "value" object. Use the offsetof() macro to fill this out. -// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter, -// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide -// this parameter to dictutil if it is possible you will realloc the array. -// -// Use DictAddValue() and DictGetValue() with this dictionary type. -extern "C" HRESULT DAPI DictCreateWithEmbeddedKey( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in_opt void **ppvArray, - __in size_t cByteOffset, - __in DICT_FLAG dfFlags - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); - - // Allocate the handle - *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); - DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); - - STRINGDICT_STRUCT *psd = static_cast(*psdHandle); - - // Fill out the new handle's values - psd->dtType = DICT_EMBEDDED_KEY; - psd->dfFlags = dfFlags; - psd->cByteOffset = cByteOffset; - psd->dwBucketSizeIndex = 0; - psd->dwNumItems = 0; - psd->ppvItemList = NULL; - psd->ppvValueArray = ppvArray; - - // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime - // array based on expected number of items and items to buckets ratio - // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end - // this loop past the end of the array! - while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && - MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) - { - ++psd->dwBucketSizeIndex; - } - - // Finally, allocate our initial buckets - psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); - DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); - -LExit: - return hr; -} - -// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type. -extern "C" HRESULT DAPI DictCreateStringList( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in DICT_FLAG dfFlags - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); - - // Allocate the handle - *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); - DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); - - STRINGDICT_STRUCT *psd = static_cast(*psdHandle); - - // Fill out the new handle's values - psd->dtType = DICT_STRING_LIST; - psd->dfFlags = dfFlags; - psd->cByteOffset = 0; - psd->dwBucketSizeIndex = 0; - psd->dwNumItems = 0; - psd->ppvItemList = NULL; - psd->ppvValueArray = NULL; - - // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime - // array based on expected number of items and items to buckets ratio - // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end - // this loop past the end of the array! - while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && - MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) - { - ++psd->dwBucketSizeIndex; - } - - // Finally, allocate our initial buckets - psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); - DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI DictCreateStringListFromArray( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray, - __in DICT_FLAG dfFlags - ) -{ - HRESULT hr = S_OK; - STRINGDICT_HANDLE sd = NULL; - - hr = DictCreateStringList(&sd, cStringArray, dfFlags); - DictExitOnFailure(hr, "Failed to create the string dictionary."); - - for (DWORD i = 0; i < cStringArray; ++i) - { - const LPCWSTR wzKey = rgwzStringArray[i]; - - hr = DictKeyExists(sd, wzKey); - if (E_NOTFOUND != hr) - { - DictExitOnFailure(hr, "Failed to check the string dictionary."); - } - else - { - hr = DictAddKey(sd, wzKey); - DictExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey); - } - } - - *psdHandle = sd; - sd = NULL; - -LExit: - ReleaseDict(sd); - - return hr; -} - -extern "C" HRESULT DAPI DictCompareStringListToArray( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cStringArray; ++i) - { - hr = DictKeyExists(sdStringList, rgwzStringArray[i]); - if (E_NOTFOUND != hr) - { - DictExitOnFailure(hr, "Failed to check the string dictionary."); - ExitFunction1(hr = S_OK); - } - } - - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH)); - -LExit: - return hr; -} - -// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) -extern "C" HRESULT DAPI DictAddKey( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR pszString - ) -{ - HRESULT hr = S_OK; - DWORD dwIndex = 0; - STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict"); - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - if (DICT_STRING_LIST != psd->dtType) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType); - } - - if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) - { - hr = GrowDictionary(psd); - if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr) - { - // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full - if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) - { - hr = S_OK; - } - } - DictExitOnFailure(hr, "Failed to grow dictionary"); - } - - hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex); - DictExitOnFailure(hr, "Failed to get index to insert into"); - - hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); - DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); - ++psd->dwNumItems; - - hr = StrAllocString(reinterpret_cast(&(psd->ppvBuckets[dwIndex])), pszString, 0); - DictExitOnFailure(hr, "Failed to allocate copy of string"); - - psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex]; - -LExit: - return hr; -} - -// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) -extern "C" HRESULT DAPI DictAddValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in void *pvValue - ) -{ - HRESULT hr = S_OK; - void *pvOffset = NULL; - LPCWSTR wzKey = NULL; - DWORD dwIndex = 0; - STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); - DictExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict"); - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - if (DICT_EMBEDDED_KEY != psd->dtType) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType); - } - - wzKey = GetKey(psd, pvValue); - DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict"); - - if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) - { - hr = GrowDictionary(psd); - if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 ) - { - // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full - if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) - { - hr = S_OK; - } - } - DictExitOnFailure(hr, "Failed to grow dictionary"); - } - - hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex); - DictExitOnFailure(hr, "Failed to get index to insert into"); - - hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); - DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); - ++psd->dwNumItems; - - pvOffset = TranslateValueToOffset(psd, pvValue); - psd->ppvBuckets[dwIndex] = pvOffset; - psd->ppvItemList[psd->dwNumItems-1] = pvOffset; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI DictGetValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR pszString, - __out void **ppvValue - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); - - const STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - if (DICT_EMBEDDED_KEY != psd->dtType) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType); - } - - hr = GetValue(psd, pszString, ppvValue); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - DictExitOnFailure(hr, "Failed to call internal GetValue()"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI DictKeyExists( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR pszString - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); - - const STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - // This works with either type of dictionary - hr = GetValue(psd, pszString, NULL); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - DictExitOnFailure(hr, "Failed to call internal GetValue()"); - -LExit: - return hr; -} - -extern "C" void DAPI DictDestroy( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle - ) -{ - DWORD i; - - STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - if (DICT_STRING_LIST == psd->dtType) - { - for (i = 0; i < psd->dwNumItems; ++i) - { - ReleaseStr(reinterpret_cast(psd->ppvItemList[i])); - } - } - - ReleaseMem(psd->ppvItemList); - ReleaseMem(psd->ppvBuckets); - ReleaseMem(psd); -} - -static HRESULT StringHash( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwNumBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwHash - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzKey = NULL; - LPWSTR sczNewKey = NULL; - DWORD result = 0; - - if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) - { - hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0); - DictExitOnFailure(hr, "Failed to convert the string to upper-case."); - - wzKey = sczNewKey; - } - else - { - wzKey = pszString; - } - - while (*wzKey) - { - result = ~(*wzKey++ * 509) + result * 65599; - } - - *pdwHash = result % dwNumBuckets; - -LExit: - ReleaseStr(sczNewKey); - - return hr; -} - -static BOOL IsMatchExact( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwMatchIndex, - __in_z LPCWSTR wzOriginalString - ) -{ - LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex])); - DWORD dwFlags = 0; - - if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) - { - dwFlags |= NORM_IGNORECASE; - } - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1)) - { - return TRUE; - } - - return FALSE; -} - -static HRESULT GetValue( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out_opt void **ppvValue - ) -{ - HRESULT hr = S_OK; - DWORD dwOriginalIndexCandidate = 0; - void *pvCandidateValue = NULL; - DWORD dwIndex = 0; - - DictExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); - DictExitOnFailure(hr, "Failed to hash the string."); - - DWORD dwIndexCandidate = dwOriginalIndexCandidate; - - pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]); - - // If no match exists in the dict - if (NULL == pvCandidateValue) - { - if (NULL != ppvValue) - { - *ppvValue = NULL; - } - ExitFunction1(hr = E_NOTFOUND); - } - - hr = GetIndex(psd, pszString, &dwIndex); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - DictExitOnFailure(hr, "Failed to find index to get"); - - if (NULL != ppvValue) - { - *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]); - } - -LExit: - if (FAILED(hr) && NULL != ppvValue) - { - *ppvValue = NULL; - } - - return hr; -} - -static HRESULT GetInsertIndex( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwBucketCount, - __in void **ppvBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ) -{ - HRESULT hr = S_OK; - DWORD dwOriginalIndexCandidate = 0; - - hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate); - DictExitOnFailure(hr, "Failed to hash the string."); - - DWORD dwIndexCandidate = dwOriginalIndexCandidate; - - // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket -#pragma prefast(push) -#pragma prefast(disable:26007) - while (NULL != ppvBuckets[dwIndexCandidate]) -#pragma prefast(pop) - { - ++dwIndexCandidate; - - // If we got to the end of the array, wrap around to zero index - if (dwIndexCandidate >= dwBucketCount) - { - dwIndexCandidate = 0; - } - - // If we wrapped all the way back around to our original index, the dict is full - throw an error - if (dwIndexCandidate == dwOriginalIndexCandidate) - { - // The dict table is full - this error seems to be a reasonably close match - hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL); - DictExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString); - } - } - - *pdwOutput = dwIndexCandidate; - -LExit: - return hr; -} - -static HRESULT GetIndex( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ) -{ - HRESULT hr = S_OK; - DWORD dwOriginalIndexCandidate = 0; - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); - DictExitOnFailure(hr, "Failed to hash the string."); - - DWORD dwIndexCandidate = dwOriginalIndexCandidate; - - while (!IsMatchExact(psd, dwIndexCandidate, pszString)) - { - ++dwIndexCandidate; - - // If we got to the end of the array, wrap around to zero index - if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) - { - dwIndexCandidate = 0; - } - - // If no match exists in the dict - if (NULL == psd->ppvBuckets[dwIndexCandidate]) - { - ExitFunction1(hr = E_NOTFOUND); - } - - // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such - if (dwIndexCandidate == dwOriginalIndexCandidate) - { - ExitFunction1(hr = E_NOTFOUND); - } - } - - *pdwOutput = dwIndexCandidate; - -LExit: - return hr; -} - -static LPCWSTR GetKey( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ) -{ - const BYTE *lpByte = reinterpret_cast(pvValue); - - if (DICT_EMBEDDED_KEY == psd->dtType) - { - void *pvKey = reinterpret_cast(reinterpret_cast(pvValue) + psd->cByteOffset); - -#pragma prefast(push) -#pragma prefast(disable:26010) - return *(reinterpret_cast(pvKey)); -#pragma prefast(pop) - } - else - { - return (reinterpret_cast(lpByte)); - } -} - -static HRESULT GrowDictionary( - __inout STRINGDICT_STRUCT *psd - ) -{ - HRESULT hr = S_OK; - DWORD dwInsertIndex = 0; - LPCWSTR wzKey = NULL; - DWORD dwNewBucketSizeIndex = 0; - size_t cbAllocSize = 0; - void **ppvNewBuckets = NULL; - - dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1; - - if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL)); - } - - hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize); - DictExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary"); - - ppvNewBuckets = static_cast(MemAlloc(cbAllocSize, TRUE)); - DictExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]); - - for (DWORD i = 0; i < psd->dwNumItems; ++i) - { - wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i])); - DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value"); - - hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex); - DictExitOnFailure(hr, "Failed to get index to insert into"); - - ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i]; - } - - psd->dwBucketSizeIndex = dwNewBucketSizeIndex; - ReleaseMem(psd->ppvBuckets); - psd->ppvBuckets = ppvNewBuckets; - ppvNewBuckets = NULL; - -LExit: - ReleaseMem(ppvNewBuckets); - - return hr; -} - -static void * TranslateOffsetToValue( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ) -{ - if (NULL == pvValue) - { - return NULL; - } - - // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value - if (NULL != psd->ppvValueArray) - { - return reinterpret_cast(reinterpret_cast(pvValue) + reinterpret_cast(*psd->ppvValueArray) - 1); - } - else - { - return pvValue; - } -} - -static void * TranslateValueToOffset( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ) -{ - if (NULL != psd->ppvValueArray) - { - // 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 - return reinterpret_cast(reinterpret_cast(pvValue) - reinterpret_cast(*psd->ppvValueArray) + 1); - } - else - { - return pvValue; - } -} diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp deleted file mode 100644 index ae2c5e1c..00000000 --- a/src/dutil/dirutil.cpp +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DirExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) -#define DirExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) -#define DirExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) -#define DirExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) -#define DirExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DIRUTIL, e, x, s, __VA_ARGS__) -#define DirExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DIRUTIL, g, x, s, __VA_ARGS__) - - -/******************************************************************* - DirExists - -*******************************************************************/ -extern "C" BOOL DAPI DirExists( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ) -{ - Assert(wzPath); - - BOOL fExists = FALSE; - - DWORD dwAttributes = ::GetFileAttributesW(wzPath); - if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here - { - ExitFunction(); - } - - if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - if (pdwAttributes) - { - *pdwAttributes = dwAttributes; - } - - fExists = TRUE; - } - -LExit: - return fExists; -} - - -/******************************************************************* - DirCreateTempPath - - *******************************************************************/ -extern "C" HRESULT DAPI DirCreateTempPath( - __in_z LPCWSTR wzPrefix, - __out_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD cchPath - ) -{ - Assert(wzPrefix); - Assert(wzPath); - - HRESULT hr = S_OK; - - WCHAR wzDir[MAX_PATH]; - WCHAR wzFile[MAX_PATH]; - DWORD cch = 0; - - cch = ::GetTempPathW(countof(wzDir), wzDir); - if (!cch || cch >= countof(wzDir)) - { - DirExitWithLastError(hr, "Failed to GetTempPath."); - } - - if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) - { - DirExitWithLastError(hr, "Failed to GetTempFileName."); - } - - hr = ::StringCchCopyW(wzPath, cchPath, wzFile); - -LExit: - return hr; -} - - -/******************************************************************* - DirEnsureExists - -*******************************************************************/ -extern "C" HRESULT DAPI DirEnsureExists( - __in_z LPCWSTR wzPath, - __in_opt LPSECURITY_ATTRIBUTES psa - ) -{ - HRESULT hr = S_OK; - UINT er; - - // try to create this directory - if (!::CreateDirectoryW(wzPath, psa)) - { - // if the directory already exists, bail - er = ::GetLastError(); - if (ERROR_ALREADY_EXISTS == er) - { - ExitFunction1(hr = S_OK); - } - 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. - { - ExitFunction1(hr = S_OK); - } - - // get the parent path and try to create it - LPWSTR pwzLastSlash = NULL; - for (LPWSTR pwz = const_cast(wzPath); *pwz; ++pwz) - { - if (*pwz == L'\\') - { - pwzLastSlash = pwz; - } - } - - // if there is no parent directory fail - DirExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); - - *pwzLastSlash = L'\0'; // null terminate the parent path - hr = DirEnsureExists(wzPath, psa); // recurse! - *pwzLastSlash = L'\\'; // put the slash back - DirExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); - - // try to create the directory now that all parents are created - if (!::CreateDirectoryW(wzPath, psa)) - { - // if the directory already exists for some reason no error - er = ::GetLastError(); - if (ERROR_ALREADY_EXISTS == er) - { - hr = S_FALSE; - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - } - else - { - hr = S_OK; - } - } - -LExit: - return hr; -} - - -/******************************************************************* - DirEnsureDelete - removes an entire directory structure - -*******************************************************************/ -extern "C" HRESULT DAPI DirEnsureDelete( - __in_z LPCWSTR wzPath, - __in BOOL fDeleteFiles, - __in BOOL fRecurse - ) -{ - HRESULT hr = S_OK; - DWORD dwDeleteFlags = 0; - - dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0; - dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0; - - hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags); - return hr; -} - - -/******************************************************************* - DirEnsureDeleteEx - removes an entire directory structure - -*******************************************************************/ -extern "C" HRESULT DAPI DirEnsureDeleteEx( - __in_z LPCWSTR wzPath, - __in DWORD dwFlags - ) -{ - Assert(wzPath && *wzPath); - - HRESULT hr = S_OK; - DWORD er; - - DWORD dwAttrib; - HANDLE hFind = INVALID_HANDLE_VALUE; - LPWSTR sczDelete = NULL; - WIN32_FIND_DATAW wfd; - - BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); - BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); - BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); - WCHAR wzTempDirectory[MAX_PATH] = { }; - WCHAR wzTempPath[MAX_PATH] = { }; - - if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) - { - er = ::GetLastError(); - if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory. - { - er = ERROR_PATH_NOT_FOUND; - } - hr = HRESULT_FROM_WIN32(er); - DirExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); - } - - if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) - { - if (dwAttrib & FILE_ATTRIBUTE_READONLY) - { - if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL)) - { - DirExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); - } - } - - // If we're deleting files and/or child directories loop through the contents of the directory. - if (fDeleteFiles || fRecurse) - { - if (fScheduleDelete) - { - if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) - { - DirExitWithLastError(hr, "Failed to get temp directory."); - } - } - - // Delete everything in this directory. - hr = PathConcat(wzPath, L"*.*", &sczDelete); - DirExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); - - hFind = ::FindFirstFileW(sczDelete, &wfd); - if (INVALID_HANDLE_VALUE == hFind) - { - DirExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); - } - - do - { - // Skip the dot directories. - if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2]))) - { - continue; - } - - // For extra safety and to silence OACR. - wfd.cFileName[MAX_PATH - 1] = L'\0'; - - hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); - DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); - - if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - hr = PathBackslashTerminate(&sczDelete); - DirExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); - - hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call - if (FAILED(hr)) - { - // if we failed to delete a subdirectory, keep trying to finish any remaining files - ExitTraceSource(DUTIL_SOURCE_DIRUTIL, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); - hr = S_OK; - } - } - else if (fDeleteFiles) // this is a file, just delete it - { - if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) - { - if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL)) - { - DirExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); - } - } - - if (!::DeleteFileW(sczDelete)) - { - if (fScheduleDelete) - { - if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) - { - DirExitWithLastError(hr, "Failed to get temp file to move to."); - } - - // Try to move the file to the temp directory then schedule for delete, - // otherwise just schedule for delete. - if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) - { - ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); - } - else - { - ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); - } - } - else - { - DirExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); - } - } - } - } while (::FindNextFileW(hFind, &wfd)); - - er = ::GetLastError(); - if (ERROR_NO_MORE_FILES == er) - { - hr = S_OK; - } - else - { - DirExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); - } - } - - if (!::RemoveDirectoryW(wzPath)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete) - { - if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) - { - hr = S_OK; - } - } - - DirExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); - } - } - else - { - hr = E_UNEXPECTED; - DirExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); - } - - Assert(S_OK == hr); - -LExit: - ReleaseFileFindHandle(hFind); - ReleaseStr(sczDelete); - - return hr; -} - - -/******************************************************************* -DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many - of its parents as possible. - - Returns: count of directories deleted. -*******************************************************************/ -extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot( - __in_z LPCWSTR wzPath, - __in DWORD /*dwFlags*/ - ) -{ - DWORD cDeletedDirs = 0; - LPWSTR sczPath = NULL; - - while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) - { - ++cDeletedDirs; - - HRESULT hr = PathGetParentPath(wzPath, &sczPath); - DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); - - wzPath = sczPath; - } - -LExit: - ReleaseStr(sczPath); - - return cDeletedDirs; -} - - -/******************************************************************* - DirGetCurrent - gets the current directory. - -*******************************************************************/ -extern "C" HRESULT DAPI DirGetCurrent( - __deref_out_z LPWSTR* psczCurrentDirectory - ) -{ - HRESULT hr = S_OK; - SIZE_T cch = 0; - - if (psczCurrentDirectory && *psczCurrentDirectory) - { - hr = StrMaxLength(*psczCurrentDirectory, &cch); - DirExitOnFailure(hr, "Failed to determine size of current directory."); - } - - DWORD cchRequired = ::GetCurrentDirectoryW((DWORD)min(DWORD_MAX, cch), 0 == cch ? NULL : *psczCurrentDirectory); - if (0 == cchRequired) - { - DirExitWithLastError(hr, "Failed to get current directory."); - } - else if (cch < cchRequired) - { - hr = StrAlloc(psczCurrentDirectory, cchRequired); - DirExitOnFailure(hr, "Failed to allocate string for current directory."); - - if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory)) - { - DirExitWithLastError(hr, "Failed to get current directory using allocated string."); - } - } - -LExit: - return hr; -} - - -/******************************************************************* - DirSetCurrent - sets the current directory. - -*******************************************************************/ -extern "C" HRESULT DAPI DirSetCurrent( - __in_z LPCWSTR wzDirectory - ) -{ - HRESULT hr = S_OK; - - if (!::SetCurrentDirectoryW(wzDirectory)) - { - DirExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); - } - -LExit: - return hr; -} diff --git a/src/dutil/dlutil.cpp b/src/dutil/dlutil.cpp deleted file mode 100644 index 70155e6f..00000000 --- a/src/dutil/dlutil.cpp +++ /dev/null @@ -1,802 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#include -#include - - -// Exit macros -#define DlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) -#define DlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) -#define DlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) -#define DlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) -#define DlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DLUTIL, e, x, s, __VA_ARGS__) -#define DlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DLUTIL, g, x, s, __VA_ARGS__) - -static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024; -static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL }; - -// internal function declarations - -static HRESULT InitializeResume( - __in LPCWSTR wzDestinationPath, - __out LPWSTR* psczResumePath, - __out HANDLE* phResumeFile, - __out DWORD64* pdw64ResumeOffset - ); -static HRESULT GetResourceMetadata( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out DWORD64* pdw64ResourceSize, - __out FILETIME* pftResourceCreated - ); -static HRESULT DownloadResource( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_z LPCWSTR wzDestinationPath, - __in DWORD64 dw64AuthoredResourceLength, - __in DWORD64 dw64ResourceLength, - __in DWORD64 dw64ResumeOffset, - __in HANDLE hResumeFile, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ); -static HRESULT AllocateRangeRequestHeader( - __in DWORD64 dw64ResumeOffset, - __in DWORD64 dw64ResourceLength, - __deref_inout_z LPWSTR* psczHeader - ); -static HRESULT WriteToFile( - __in HINTERNET hUrl, - __in HANDLE hPayloadFile, - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD64 dw64ResourceLength, - __in LPBYTE pbData, - __in DWORD cbData, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback - ); -static HRESULT UpdateResumeOffset( - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD cbData - ); -static HRESULT MakeRequest( - __in HINTERNET hSession, - __inout_z LPWSTR* psczSourceUrl, - __in_z_opt LPCWSTR wzMethod, - __in_z_opt LPCWSTR wzHeaders, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out HINTERNET* phConnect, - __out HINTERNET* phUrl, - __out BOOL* pfRangeRequestsAccepted - ); -static HRESULT OpenRequest( - __in HINTERNET hConnect, - __in_z_opt LPCWSTR wzMethod, - __in INTERNET_SCHEME scheme, - __in_z LPCWSTR wzResource, - __in_z_opt LPCWSTR wzQueryString, - __in_z_opt LPCWSTR wzHeader, - __out HINTERNET* phUrl - ); -static HRESULT SendRequest( - __in HINTERNET hUrl, - __inout_z LPWSTR* psczUrl, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetry, - __out BOOL* pfRangesAccepted - ); -static HRESULT AuthenticationRequired( - __in HINTERNET hUrl, - __in long lHttpCode, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ); -static HRESULT DownloadGetResumePath( - __in_z LPCWSTR wzPayloadWorkingPath, - __deref_out_z LPWSTR* psczResumePath - ); -static HRESULT DownloadSendProgressCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in HANDLE hDestinationFile - ); -// function definitions - -extern "C" HRESULT DAPI DownloadUrl( - __in DOWNLOAD_SOURCE* pDownloadSource, - __in DWORD64 dw64AuthoredDownloadSize, - __in LPCWSTR wzDestinationPath, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ) -{ - HRESULT hr = S_OK; - LPWSTR sczUrl = NULL; - HINTERNET hSession = NULL; - DWORD dwTimeout = 0; - LPWSTR sczResumePath = NULL; - HANDLE hResumeFile = INVALID_HANDLE_VALUE; - DWORD64 dw64ResumeOffset = 0; - DWORD64 dw64Size = 0; - FILETIME ftCreated = { }; - - // Copy the download source into a working variable to handle redirects then - // open the internet session. - hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); - DlExitOnFailure(hr, "Failed to copy download source URL."); - - hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); - DlExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); - - // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. - PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); - if (0 < dwTimeout) - { - dwTimeout *= 1000; // convert to milliseconds. - ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); - ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); - ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); - } - - // Get the resource size and creation time from the internet. - hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated); - DlExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl); - - // Ignore failure to initialize resume because we will fall back to full download then - // download. - InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); - - hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate); - DlExitOnFailure(hr, "Failed to download URL: %ls", sczUrl); - - // Cleanup the resume file because we successfully downloaded the whole file. - if (sczResumePath && *sczResumePath) - { - ::DeleteFileW(sczResumePath); - } - -LExit: - ReleaseFileHandle(hResumeFile); - ReleaseStr(sczResumePath); - ReleaseInternet(hSession); - ReleaseStr(sczUrl); - - return hr; -} - - -// internal helper functions - -static HRESULT InitializeResume( - __in LPCWSTR wzDestinationPath, - __out LPWSTR* psczResumePath, - __out HANDLE* phResumeFile, - __out DWORD64* pdw64ResumeOffset - ) -{ - HRESULT hr = S_OK; - HANDLE hResumeFile = INVALID_HANDLE_VALUE; - DWORD cbTotalReadResumeData = 0; - DWORD cbReadData = 0; - - *pdw64ResumeOffset = 0; - - hr = DownloadGetResumePath(wzDestinationPath, psczResumePath); - DlExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); - - hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hResumeFile) - { - DlExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath); - } - - do - { - if (!::ReadFile(hResumeFile, reinterpret_cast(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) - { - DlExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath); - } - cbTotalReadResumeData += cbReadData; - } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); - - // Start over if we couldn't get a resume offset. - if (cbTotalReadResumeData != sizeof(DWORD64)) - { - *pdw64ResumeOffset = 0; - } - - *phResumeFile = hResumeFile; - hResumeFile = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hResumeFile); - return hr; -} - -static HRESULT GetResourceMetadata( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out DWORD64* pdw64ResourceSize, - __out FILETIME* pftResourceCreated - ) -{ - HRESULT hr = S_OK; - BOOL fRangeRequestsAccepted = TRUE; - HINTERNET hConnect = NULL; - HINTERNET hUrl = NULL; - LONGLONG llLength = 0; - - hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); - DlExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl); - - hr = InternetGetSizeByHandle(hUrl, &llLength); - if (FAILED(hr)) - { - llLength = 0; - hr = S_OK; - } - - *pdw64ResourceSize = llLength; - - // Get the last modified time from the server, we'll use that as our downloaded time here. If - // the server time isn't available then use the local system time. - hr = InternetGetCreateTimeByHandle(hUrl, pftResourceCreated); - if (FAILED(hr)) - { - ::GetSystemTimeAsFileTime(pftResourceCreated); - hr = S_OK; - } - -LExit: - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - return hr; -} - -static HRESULT DownloadResource( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_z LPCWSTR wzDestinationPath, - __in DWORD64 dw64AuthoredResourceLength, - __in DWORD64 dw64ResourceLength, - __in DWORD64 dw64ResumeOffset, - __in HANDLE hResumeFile, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ) -{ - HRESULT hr = S_OK; - HANDLE hPayloadFile = INVALID_HANDLE_VALUE; - DWORD cbMaxData = 64 * 1024; // 64 KB - BYTE* pbData = NULL; - BOOL fRangeRequestsAccepted = TRUE; - LPWSTR sczRangeRequestHeader = NULL; - HINTERNET hConnect = NULL; - HINTERNET hUrl = NULL; - LONGLONG llLength = 0; - - hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hPayloadFile) - { - DlExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath); - } - - // Allocate a memory block on a page boundary in case we want to do optimal writing. - pbData = static_cast(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); - DlExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); - - // Let's try downloading the file assuming that range requests are accepted. If range requests - // are not supported we'll have to start over and accept the fact that we only get one shot - // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't - // like files that big. - while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) - { - hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); - DlExitOnFailure(hr, "Failed to allocate range request header."); - - ReleaseNullInternet(hConnect); - ReleaseNullInternet(hUrl); - - hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); - DlExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl); - - // If we didn't get the size of the resource from the initial "HEAD" request - // then let's try to get the size from this "GET" request. - if (0 == dw64ResourceLength) - { - hr = InternetGetSizeByHandle(hUrl, &llLength); - if (SUCCEEDED(hr)) - { - dw64ResourceLength = llLength; - } - else // server didn't tell us the resource length. - { - // Fallback to the authored size of the resource. However, since we - // don't really know the size on the server, don't try to use - // range requests either. - dw64ResourceLength = dw64AuthoredResourceLength; - fRangeRequestsAccepted = FALSE; - } - } - - // If we just tried to do a range request and found out that it isn't supported, start over. - if (!fRangeRequestsAccepted) - { - // TODO: log a message that the server did not accept range requests. - dw64ResumeOffset = 0; - } - - hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache); - DlExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); - } - -LExit: - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - ReleaseStr(sczRangeRequestHeader); - if (pbData) - { - ::VirtualFree(pbData, 0, MEM_RELEASE); - } - ReleaseFileHandle(hPayloadFile); - - return hr; -} - -static HRESULT AllocateRangeRequestHeader( - __in DWORD64 dw64ResumeOffset, - __in DWORD64 dw64ResourceLength, - __deref_inout_z LPWSTR* psczHeader - ) -{ - HRESULT hr = S_OK; - - // If the remaining length is less that 2GB we'll be able to ask for everything. - DWORD64 dw64RemainingLength = dw64ResourceLength - dw64ResumeOffset; - if (DOWNLOAD_ENGINE_TWO_GIGABYTES > dw64RemainingLength) - { - // If we have a resume offset, let's download everything from there. Otherwise, we'll - // just get everything with no headers in the way. - if (0 < dw64ResumeOffset) - { - hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset); - DlExitOnFailure(hr, "Failed to add range read header."); - } - else - { - ReleaseNullStr(*psczHeader); - } - } - else // we'll have to download in chunks. - { - hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1); - DlExitOnFailure(hr, "Failed to add range read header."); - } - -LExit: - return hr; -} - -static HRESULT WriteToFile( - __in HINTERNET hUrl, - __in HANDLE hPayloadFile, - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD64 dw64ResourceLength, - __in LPBYTE pbData, - __in DWORD cbData, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback - ) -{ - HRESULT hr = S_OK; - DWORD cbReadData = 0; - - hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN); - DlExitOnFailure(hr, "Failed to seek to start point in file."); - - do - { - // Read bits from the internet. - if (!::InternetReadFile(hUrl, static_cast(pbData), cbData, &cbReadData)) - { - DlExitWithLastError(hr, "Failed while reading from internet."); - } - - // Write bits to disk (if there are any). - if (cbReadData) - { - DWORD cbTotalWritten = 0; - DWORD cbWritten = 0; - do - { - if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL)) - { - DlExitWithLastError(hr, "Failed to write data from internet."); - } - - cbTotalWritten += cbWritten; - } while (cbWritten && cbTotalWritten < cbReadData); - - // Ignore failure from updating resume file as this doesn't mean the download cannot succeed. - UpdateResumeOffset(pdw64ResumeOffset, hResumeFile, cbTotalWritten); - - if (pCallback && pCallback->pfnProgress) - { - hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile); - DlExitOnFailure(hr, "UX aborted on cache progress."); - } - } - } while (cbReadData); - -LExit: - return hr; -} - -static HRESULT UpdateResumeOffset( - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD cbData - ) -{ - HRESULT hr = S_OK; - - *pdw64ResumeOffset += cbData; - - if (INVALID_HANDLE_VALUE != hResumeFile) - { - DWORD cbTotalWrittenResumeData = 0; - DWORD cbWrittenResumeData = 0; - - hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN); - DlExitOnFailure(hr, "Failed to seek to start point in file."); - - do - { - // Ignore failure to write to the resume file as that should not prevent the download from happening. - if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL)) - { - DlExitOnFailure(hr, "Failed to seek to write to file."); - } - - cbTotalWrittenResumeData += cbWrittenResumeData; - } while (cbWrittenResumeData && sizeof(DWORD64) > cbTotalWrittenResumeData); - } - -LExit: - return hr; -} - -static HRESULT MakeRequest( - __in HINTERNET hSession, - __inout_z LPWSTR* psczSourceUrl, - __in_z_opt LPCWSTR wzMethod, - __in_z_opt LPCWSTR wzHeaders, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out HINTERNET* phConnect, - __out HINTERNET* phUrl, - __out BOOL* pfRangeRequestsAccepted - ) -{ - HRESULT hr = S_OK; - HINTERNET hConnect = NULL; - HINTERNET hUrl = NULL; - URI_INFO uri = { }; - - // Try to open the URL. - BOOL fRetry; - do - { - fRetry = FALSE; - - // If the URL was opened close it, so we can reopen it again. - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - - // Open the url. - hr = UriCrackEx(*psczSourceUrl, &uri); - DlExitOnFailure(hr, "Failed to break URL into server and resource parts."); - - 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); - DlExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); - - // Best effort set the proxy username and password, if they were provided. - if ((wzUser && *wzUser) && (wzPassword && *wzPassword)) - { - if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser))) - { - ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword)); - } - } - - hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl); - DlExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl); - - hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted); - DlExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl); - } while (fRetry); - - // Okay, we're all ready to start downloading. Update the connection information. - *phConnect = hConnect; - hConnect = NULL; - *phUrl = hUrl; - hUrl = NULL; - -LExit: - UriInfoUninitialize(&uri); - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - - return hr; -} - -static HRESULT OpenRequest( - __in HINTERNET hConnect, - __in_z_opt LPCWSTR wzMethod, - __in INTERNET_SCHEME scheme, - __in_z LPCWSTR wzResource, - __in_z_opt LPCWSTR wzQueryString, - __in_z_opt LPCWSTR wzHeader, - __out HINTERNET* phUrl - ) -{ - HRESULT hr = S_OK; - DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD; - LPWSTR sczResource = NULL; - HINTERNET hUrl = NULL; - - if (INTERNET_SCHEME_HTTPS == scheme) - { - dwRequestFlags |= INTERNET_FLAG_SECURE; - } - else if (INTERNET_SCHEME_HTTP == scheme) - { - dwRequestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; - } - - // Allocate the resource name. - hr = StrAllocString(&sczResource, wzResource, 0); - DlExitOnFailure(hr, "Failed to allocate string for resource URI."); - - if (wzQueryString && *wzQueryString) - { - hr = StrAllocConcat(&sczResource, wzQueryString, 0); - DlExitOnFailure(hr, "Failed to append query strong to resource from URI."); - } - - // Open the request and add the header if provided. - hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL); - DlExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); - - if (wzHeader && *wzHeader) - { - if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast(-1), HTTP_ADDREQ_FLAG_COALESCE)) - { - DlExitWithLastError(hr, "Failed to add header to HTTP request."); - } - } - - *phUrl = hUrl; - hUrl = NULL; - -LExit: - ReleaseInternet(hUrl); - ReleaseStr(sczResource); - return hr; -} - -static HRESULT SendRequest( - __in HINTERNET hUrl, - __inout_z LPWSTR* psczUrl, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetry, - __out BOOL* pfRangesAccepted - ) -{ - HRESULT hr = S_OK; - BOOL fRetrySend = FALSE; - LONG lCode = 0; - - do - { - fRetrySend = FALSE; - - if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it. - LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl); - - // Try to get the HTTP status code and, if good, handle via the switch statement below but if it - // fails return the error code from the send request above as the result of the function. - HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); - DlExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); - } - else // get the http status code. - { - hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); - DlExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); - } - - switch (lCode) - { - case 200: // OK but range requests don't work. - *pfRangesAccepted = FALSE; - hr = S_OK; - break; - - case 206: // Partial content means that range requests work! - *pfRangesAccepted = TRUE; - hr = S_OK; - break; - - // redirection cases - case 301: __fallthrough; // file moved - case 302: __fallthrough; // temporary - case 303: // redirect method - hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl); - DlExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl); - - *pfRetry = TRUE; - break; - - // error cases - case 400: // bad request - hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); - break; - - case 401: __fallthrough; // unauthorized - case 407: __fallthrough; // proxy unauthorized - hr = AuthenticationRequired(hUrl, lCode, pAuthenticate, &fRetrySend, pfRetry); - break; - - case 403: // forbidden - hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - break; - - case 404: // file not found - case 410: // gone - hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - break; - - case 405: // method not allowed - hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - break; - - case 408: __fallthrough; // request timedout - case 504: // gateway timeout - hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); - break; - - case 414: // request URI too long - hr = CO_E_PATHTOOLONG; - break; - - case 502: __fallthrough; // server (through a gateway) was not found - case 503: // server unavailable - hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); - break; - - case 418: // I'm a teapot. - default: - // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway) - // do not overwrite the error code from the failed request. Otherwise, the error was unexpected. - if (SUCCEEDED(hr)) - { - hr = E_UNEXPECTED; - } - - LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl); - break; - } - } while (fRetrySend); - -LExit: - return hr; -} - -static HRESULT AuthenticationRequired( - __in HINTERNET hUrl, - __in long lHttpCode, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ) -{ - Assert(401 == lHttpCode || 407 == lHttpCode); - - HRESULT hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - *pfRetrySend = FALSE; - *pfRetry = FALSE; - - if (pAuthenticate && pAuthenticate->pfnAuthenticate) - { - hr = (*pAuthenticate->pfnAuthenticate)(pAuthenticate->pv, hUrl, lHttpCode, pfRetrySend, pfRetry); - } - - return hr; -} - - -static HRESULT DownloadGetResumePath( - __in_z LPCWSTR wzPayloadWorkingPath, - __deref_out_z LPWSTR* psczResumePath - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); - DlExitOnFailure(hr, "Failed to create resume path."); - -LExit: - return hr; -} - -static HRESULT DownloadSendProgressCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in HANDLE hDestinationFile - ) -{ - static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; - - HRESULT hr = S_OK; - DWORD dwResult = PROGRESS_CONTINUE; - LARGE_INTEGER liTotalSize = { }; - LARGE_INTEGER liTotalTransferred = { }; - - if (pCallback->pfnProgress) - { - liTotalSize.QuadPart = dw64Total; - liTotalTransferred.QuadPart = dw64Progress; - - dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); - switch (dwResult) - { - case PROGRESS_CONTINUE: - hr = S_OK; - break; - - case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? - case PROGRESS_STOP: - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - DlExitOnRootFailure(hr, "UX aborted on download progress."); - - case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. - pCallback->pfnProgress = NULL; - hr = S_OK; - break; - - default: - hr = E_UNEXPECTED; - DlExitOnRootFailure(hr, "Invalid return code from progress routine."); - } - } - -LExit: - return hr; -} diff --git a/src/dutil/dpiutil.cpp b/src/dutil/dpiutil.cpp deleted file mode 100644 index 4096c8d3..00000000 --- a/src/dutil/dpiutil.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) -#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) -#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) -#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) -#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__) - -static PFN_ADJUSTWINDOWRECTEXFORDPI vpfnAdjustWindowRectExForDpi = NULL; -static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; -static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; -static PFN_SETPROCESSDPIAWARE vpfnSetProcessDPIAware = NULL; -static PFN_SETPROCESSDPIAWARENESS vpfnSetProcessDpiAwareness = NULL; -static PFN_SETPROCESSDPIAWARENESSCONTEXT vpfnSetProcessDpiAwarenessContext = NULL; - -static HMODULE vhShcoreDll = NULL; -static HMODULE vhUser32Dll = NULL; -static BOOL vfDpiuInitialized = FALSE; - -DAPI_(void) DpiuInitialize() -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll); - if (SUCCEEDED(hr)) - { - // Ignore failures. - vpfnGetDpiForMonitor = reinterpret_cast(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor")); - vpfnSetProcessDpiAwareness = reinterpret_cast(::GetProcAddress(vhShcoreDll, "SetProcessDpiAwareness")); - } - - hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll); - if (SUCCEEDED(hr)) - { - // Ignore failures. - vpfnAdjustWindowRectExForDpi = reinterpret_cast(::GetProcAddress(vhUser32Dll, "AdjustWindowRectExForDpi")); - vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); - vpfnSetProcessDPIAware = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDPIAware")); - vpfnSetProcessDpiAwarenessContext = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDpiAwarenessContext")); - } - - vfDpiuInitialized = TRUE; -} - -DAPI_(void) DpiuUninitialize() -{ - if (vhShcoreDll) - { - ::FreeLibrary(vhShcoreDll); - } - - if (vhUser32Dll) - { - ::FreeLibrary(vhUser32Dll); - } - - vhShcoreDll = NULL; - vhUser32Dll = NULL; - vpfnAdjustWindowRectExForDpi = NULL; - vpfnGetDpiForMonitor = NULL; - vpfnGetDpiForWindow = NULL; - vfDpiuInitialized = FALSE; -} - -DAPI_(void) DpiuAdjustWindowRect( - __in RECT* pWindowRect, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle, - __in UINT nDpi - ) -{ - if (WS_SYSMENU & dwStyle) - { - dwStyle |= WS_CAPTION; // WS_CAPTION is required with WS_SYSMENU, AdjustWindowRect* won't work properly when it's not specified. - } - - if (vpfnAdjustWindowRectExForDpi) - { - vpfnAdjustWindowRectExForDpi(pWindowRect, dwStyle, fMenu, dwExStyle, nDpi); - } - else - { - ::AdjustWindowRectEx(pWindowRect, dwStyle, fMenu, dwExStyle); - } -} - -DAPI_(HRESULT) DpiuGetMonitorContextFromPoint( - __in const POINT* pt, - __out DPIU_MONITOR_CONTEXT** ppMonitorContext - ) -{ - HRESULT hr = S_OK; - DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; - HMONITOR hMonitor = NULL; - UINT dpiX = 0; - UINT dpiY = 0; - HDC hdc = NULL; - - pMonitorContext = reinterpret_cast(MemAlloc(sizeof(DPIU_MONITOR_CONTEXT), TRUE)); - DpiuExitOnNull(pMonitorContext, hr, E_OUTOFMEMORY, "Failed to allocate memory for DpiuMonitorContext."); - - hMonitor = ::MonitorFromPoint(*pt, MONITOR_DEFAULTTONEAREST); - DpiuExitOnNull(hMonitor, hr, E_FAIL, "Failed to get monitor from point."); - - pMonitorContext->mi.cbSize = sizeof(pMonitorContext->mi); - if (!::GetMonitorInfoW(hMonitor, &pMonitorContext->mi)) - { - DpiuExitOnFailure(hr = E_OUTOFMEMORY, "Failed to get monitor info for point."); - } - - if (vpfnGetDpiForMonitor) - { - hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - DpiuExitOnFailure(hr, "Failed to get DPI for monitor."); - - pMonitorContext->nDpi = dpiX; - } - else - { - hdc = ::CreateDCW(L"DISPLAY", pMonitorContext->mi.szDevice, NULL, NULL); - DpiuExitOnNull(hdc, hr, E_OUTOFMEMORY, "Failed to get device context for monitor."); - - pMonitorContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); - } - - *ppMonitorContext = pMonitorContext; - pMonitorContext = NULL; - -LExit: - if (hdc) - { - ::ReleaseDC(NULL, hdc); - } - - MemFree(pMonitorContext); - - return hr; -} - -DAPI_(void) DpiuGetWindowContext( - __in HWND hWnd, - __in DPIU_WINDOW_CONTEXT* pWindowContext - ) -{ - HRESULT hr = S_OK; - HMONITOR hMonitor = NULL; - UINT dpiX = 0; - UINT dpiY = 0; - HDC hdc = NULL; - - pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI; - - if (vpfnGetDpiForWindow) - { - pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd); - ExitFunction(); - } - - if (vpfnGetDpiForMonitor) - { - hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); - if (hMonitor) - { - hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - if (SUCCEEDED(hr)) - { - pWindowContext->nDpi = dpiX; - ExitFunction(); - } - } - } - - hdc = ::GetDC(hWnd); - if (hdc) - { - pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); - } - -LExit: - if (hdc) - { - ::ReleaseDC(hWnd, hdc); - } -} - -DAPI_(int) DpiuScaleValue( - __in int nDefaultDpiValue, - __in UINT nTargetDpi - ) -{ - return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI); -} - -DAPI_(HRESULT) DpiuSetProcessDpiAwareness( - __in DPIU_AWARENESS supportedAwareness, - __in_opt DPIU_AWARENESS* pSelectedAwareness - ) -{ - HRESULT hr = S_OK; - DPIU_AWARENESS selectedAwareness = DPIU_AWARENESS_NONE; - DPI_AWARENESS_CONTEXT awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE; - PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE; - - if (vpfnSetProcessDpiAwarenessContext) - { - if (DPIU_AWARENESS_PERMONITORV2 & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; - selectedAwareness = DPIU_AWARENESS_PERMONITORV2; - } - else if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; - selectedAwareness = DPIU_AWARENESS_PERMONITOR; - } - else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; - selectedAwareness = DPIU_AWARENESS_SYSTEM; - } - else if (DPIU_AWARENESS_GDISCALED & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED; - selectedAwareness = DPIU_AWARENESS_GDISCALED; - } - - if (!vpfnSetProcessDpiAwarenessContext(awarenessContext)) - { - DpiuExitOnLastError(hr, "Failed to set process DPI awareness context."); - } - } - else if (vpfnSetProcessDpiAwareness) - { - if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) - { - awareness = PROCESS_PER_MONITOR_DPI_AWARE; - selectedAwareness = DPIU_AWARENESS_PERMONITOR; - } - else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) - { - awareness = PROCESS_SYSTEM_DPI_AWARE; - selectedAwareness = DPIU_AWARENESS_SYSTEM; - } - - hr = vpfnSetProcessDpiAwareness(awareness); - DpiuExitOnFailure(hr, "Failed to set process DPI awareness."); - } - else if (vpfnSetProcessDPIAware && (DPIU_AWARENESS_SYSTEM & supportedAwareness)) - { - selectedAwareness = DPIU_AWARENESS_SYSTEM; - if (!vpfnSetProcessDPIAware()) - { - DpiuExitOnLastError(hr, "Failed to set process DPI aware."); - } - } - -LExit: - if (pSelectedAwareness) - { - *pSelectedAwareness = selectedAwareness; - } - - return hr; -} diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp deleted file mode 100644 index 56b85207..00000000 --- a/src/dutil/dutil.cpp +++ /dev/null @@ -1,524 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) -#define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) -#define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) -#define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) -#define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__) -#define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__) - -// No need for OACR to warn us about using non-unicode APIs in this file. -#pragma prefast(disable:25068) - -// Asserts & Tracing - -const int DUTIL_STRING_BUFFER = 1024; -static HMODULE Dutil_hAssertModule = NULL; -static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL; -static BOOL Dutil_fNoAsserts = FALSE; -static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD; -static BOOL Dutil_fTraceFilenames = FALSE; -static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL; - - -DAPI_(HRESULT) DutilInitialize( - __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback - ) -{ - HRESULT hr = S_OK; - - vpfnTraceErrorCallback = pfnTraceErrorCallback; - - return hr; -} - - -DAPI_(void) DutilUninitialize() -{ - vpfnTraceErrorCallback = NULL; -} - -/******************************************************************* -Dutil_SetAssertModule - -*******************************************************************/ -extern "C" void DAPI Dutil_SetAssertModule( - __in HMODULE hAssertModule - ) -{ - Dutil_hAssertModule = hAssertModule; -} - - -/******************************************************************* -Dutil_SetAssertDisplayFunction - -*******************************************************************/ -extern "C" void DAPI Dutil_SetAssertDisplayFunction( - __in DUTIL_ASSERTDISPLAYFUNCTION pfn - ) -{ - Dutil_pfnDisplayAssert = pfn; -} - - -/******************************************************************* -Dutil_AssertMsg - -*******************************************************************/ -extern "C" void DAPI Dutil_AssertMsg( - __in_z LPCSTR szMessage - ) -{ - static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts) - - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - int id = IDRETRY; - HKEY hkDebug = NULL; - HANDLE hAssertFile = INVALID_HANDLE_VALUE; - char szPath[MAX_PATH] = { }; - DWORD cch = 0; - - if (fInAssert) - { - return; - } - fInAssert = TRUE; - - char szMsg[DUTIL_STRING_BUFFER]; - hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); - DExitOnFailure(hr, "failed to copy message while building assert message"); - - if (Dutil_pfnDisplayAssert) - { - // call custom function to display the assert string - if (!Dutil_pfnDisplayAssert(szMsg)) - { - ExitFunction(); - } - } - else - { - OutputDebugStringA(szMsg); - } - - if (!Dutil_fNoAsserts) - { - er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug); - if (ERROR_SUCCESS == er) - { - cch = countof(szPath); - er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast(szPath), &cch); - szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that. - if (ERROR_SUCCESS == er) - { - hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE != hAssertFile) - { - if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END)) - { - if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n"))) - { - ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); - } - } - } - } - } - - // if anything went wrong while fooling around with the registry, just show the usual assert dialog box - if (ERROR_SUCCESS != er) - { - hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); - DExitOnFailure(hr, "failed to concat string while building assert message"); - - id = ::MessageBoxA(0, szMsg, "Debug Assert Message", - MB_SERVICE_NOTIFICATION | MB_TOPMOST | - MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE); - } - } - - if (id == IDABORT) - { - if (Dutil_hAssertModule) - { - ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath)); - - 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()); - if (SUCCEEDED(hr)) - { - ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK); - } - } - - ::DebugBreak(); - } - else if (id == IDIGNORE) - { - Dutil_fNoAsserts = TRUE; - } - -LExit: - ReleaseFileHandle(hAssertFile); - ReleaseRegKey(hkDebug); - fInAssert = FALSE; -} - - -/******************************************************************* -Dutil_Assert - -*******************************************************************/ -extern "C" void DAPI Dutil_Assert( - __in_z LPCSTR szFile, - __in int iLine - ) -{ - HRESULT hr = S_OK; - char szMessage[DUTIL_STRING_BUFFER] = { }; - hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine); - if (SUCCEEDED(hr)) - { - Dutil_AssertMsg(szMessage); - } - else - { - Dutil_AssertMsg("Assert failed to build string"); - } -} - - -/******************************************************************* -Dutil_AssertSz - -*******************************************************************/ -extern "C" void DAPI Dutil_AssertSz( - __in_z LPCSTR szFile, - __in int iLine, - __in_z __format_string LPCSTR szMsg - ) -{ - HRESULT hr = S_OK; - char szMessage[DUTIL_STRING_BUFFER] = { }; - - hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg); - if (SUCCEEDED(hr)) - { - Dutil_AssertMsg(szMessage); - } - else - { - Dutil_AssertMsg("Assert failed to build string"); - } -} - - -/******************************************************************* -Dutil_TraceSetLevel - -*******************************************************************/ -extern "C" void DAPI Dutil_TraceSetLevel( - __in REPORT_LEVEL rl, - __in BOOL fTraceFilenames - ) -{ - Dutil_rlCurrentTrace = rl; - Dutil_fTraceFilenames = fTraceFilenames; -} - - -/******************************************************************* -Dutil_TraceGetLevel - -*******************************************************************/ -extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel() -{ - return Dutil_rlCurrentTrace; -} - - -/******************************************************************* -Dutil_Trace - -*******************************************************************/ -extern "C" void DAPIV Dutil_Trace( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level"); - - HRESULT hr = S_OK; - char szOutput[DUTIL_STRING_BUFFER] = { }; - char szMsg[DUTIL_STRING_BUFFER] = { }; - - if (Dutil_rlCurrentTrace < rl) - { - return; - } - - va_list args; - va_start(args, szFormat); - hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); - va_end(args); - - if (SUCCEEDED(hr)) - { - LPCSTR szPrefix = "Trace/u"; - switch (rl) - { - case REPORT_STANDARD: - szPrefix = "Trace/s"; - break; - case REPORT_VERBOSE: - szPrefix = "Trace/v"; - break; - case REPORT_DEBUG: - szPrefix = "Trace/d"; - break; - } - - if (Dutil_fTraceFilenames) - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput); - } - else - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput); - } - - if (SUCCEEDED(hr)) - { - OutputDebugStringA(szMsg); - } - // else fall through to the case below - } - - if (FAILED(hr)) - { - if (Dutil_fTraceFilenames) - { - ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine); - } - else - { - ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n"); - } - - szMsg[countof(szMsg)-1] = '\0'; - OutputDebugStringA(szMsg); - } -} - - -/******************************************************************* -Dutil_TraceError - -*******************************************************************/ -extern "C" void DAPIV Dutil_TraceError( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - char szOutput[DUTIL_STRING_BUFFER] = { }; - char szMsg[DUTIL_STRING_BUFFER] = { }; - - // if this is NOT an error report and we're not logging at this level, bail - if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) - { - return; - } - - va_list args; - va_start(args, szFormat); - hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); - va_end(args); - - if (SUCCEEDED(hr)) - { - if (Dutil_fTraceFilenames) - { - if (FAILED(hrError)) - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput); - } - else - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput); - } - } - else - { - if (FAILED(hrError)) - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput); - } - else - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput); - } - } - - if (SUCCEEDED(hr)) - { - OutputDebugStringA(szMsg); - } - // else fall through to the failure case below - } - - if (FAILED(hr)) - { - if (Dutil_fTraceFilenames) - { - if (FAILED(hrError)) - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine); - } - else - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine); - } - } - else - { - if (FAILED(hrError)) - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError); - } - else - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n"); - } - } - - szMsg[countof(szMsg)-1] = '\0'; - OutputDebugStringA(szMsg); - } -} - - -DAPIV_(void) Dutil_TraceErrorSource( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in UINT source, - __in HRESULT hr, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - // if this is NOT an error report and we're not logging at this level, bail - if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) - { - return; - } - - if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback) - { - va_list args; - va_start(args, szFormat); - vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args); - va_end(args); - } -} - - -/******************************************************************* -Dutil_RootFailure - -*******************************************************************/ -extern "C" void DAPI Dutil_RootFailure( - __in_z LPCSTR szFile, - __in int iLine, - __in HRESULT hrError - ) -{ -#ifndef DEBUG - UNREFERENCED_PARAMETER(szFile); - UNREFERENCED_PARAMETER(iLine); - UNREFERENCED_PARAMETER(hrError); -#endif // DEBUG - - TraceError(hrError, "Root failure at %s:%d", szFile, iLine); -} - -/******************************************************************* - LoadSystemLibrary - Fully qualifies the path to a module in the - Windows system directory and loads it. - - Returns - E_MODNOTFOUND - The module could not be found. - * - Another error occured. -********************************************************************/ -extern "C" HRESULT DAPI LoadSystemLibrary( - __in_z LPCWSTR wzModuleName, - __out HMODULE *phModule - ) -{ - HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL); - return hr; -} - -/******************************************************************* - LoadSystemLibraryWithPath - Fully qualifies the path to a module in - the Windows system directory and loads it - and returns the path - - Returns - E_MODNOTFOUND - The module could not be found. - * - Another error occured. -********************************************************************/ -extern "C" HRESULT DAPI LoadSystemLibraryWithPath( - __in_z LPCWSTR wzModuleName, - __out HMODULE *phModule, - __deref_out_z_opt LPWSTR* psczPath - ) -{ - HRESULT hr = S_OK; - DWORD cch = 0; - WCHAR wzPath[MAX_PATH] = { }; - - cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); - DExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); - - if (L'\\' != wzPath[cch - 1]) - { - hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); - DExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); - } - - hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); - DExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); - - *phModule = ::LoadLibraryW(wzPath); - DExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); - - if (psczPath) - { - hr = StrAllocString(psczPath, wzPath, MAX_PATH); - DExitOnFailure(hr, "Failed to copy the path to library."); - } - -LExit: - return hr; -} diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec deleted file mode 100644 index 3499a2d5..00000000 --- a/src/dutil/dutil.nuspec +++ /dev/null @@ -1,27 +0,0 @@ - - - - $id$ - $version$ - $authors$ - $authors$ - MS-RL - https://github.com/wixtoolset/dutil - false - $description$ - $copyright$ - - - - - - - - - - - - - - - diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj deleted file mode 100644 index 4e341438..00000000 --- a/src/dutil/dutil.vcxproj +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - Debug - ARM64 - - - Debug - Win32 - - - Release - ARM64 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - - {1244E671-F108-4334-BA52-8A7517F26ECD} - StaticLibrary - dutil - true - v142 - MultiByte - WiX Toolset native library foundation - WixToolset.DUtil - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - 4091;4458 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters deleted file mode 100644 index b93d166b..00000000 --- a/src/dutil/dutil.vcxproj.filters +++ /dev/null @@ -1,372 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Header Files - - - - \ No newline at end of file diff --git a/src/dutil/eseutil.cpp b/src/dutil/eseutil.cpp deleted file mode 100644 index b9455d4b..00000000 --- a/src/dutil/eseutil.cpp +++ /dev/null @@ -1,1340 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define EseExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) -#define EseExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) -#define EseExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) -#define EseExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) -#define EseExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ESEUTIL, e, x, s, __VA_ARGS__) -#define EseExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ESEUTIL, g, x, s, __VA_ARGS__) - -struct ESE_QUERY -{ - ESE_QUERY_TYPE qtQueryType; - BOOL fIndexRangeSet; - - JET_SESID jsSession; - JET_TABLEID jtTable; - - DWORD dwColumns; - void *pvData[10]; // The data queried for for this column - DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData -}; - -// Todo: convert more JET_ERR to HRESULTS here -HRESULT HresultFromJetError(JET_ERR jEr) -{ - HRESULT hr = S_OK; - - switch (jEr) - { - case JET_errSuccess: - return S_OK; - - case JET_wrnNyi: - return E_NOTIMPL; - break; - - case JET_errOutOfMemory: - hr = E_OUTOFMEMORY; - break; - - case JET_errInvalidParameter: __fallthrough; - case JET_errInvalidInstance: - hr = E_INVALIDARG; - break; - - case JET_errDatabaseInUse: - hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE); - break; - - case JET_errKeyDuplicate: - hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); - break; - - case JET_errInvalidSystemPath: __fallthrough; - case JET_errInvalidLogDirectory: __fallthrough; - case JET_errInvalidPath: __fallthrough; - case JET_errDatabaseInvalidPath: - hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); - break; - - case JET_errDatabaseLocked: - hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT); - break; - - case JET_errInvalidDatabase: - hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION); - break; - - case JET_errCallbackNotResolved: - hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); - break; - - case JET_errNoCurrentRecord: __fallthrough; - case JET_errRecordNotFound: __fallthrough; - case JET_errFileNotFound: __fallthrough; - case JET_errObjectNotFound: - hr = E_NOTFOUND; - break; - - case JET_wrnBufferTruncated: - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - break; - - case JET_errFileAccessDenied: - hr = E_ACCESSDENIED; - break; - - default: - hr = E_FAIL; - } - - // 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 - ExitTraceSource(DUTIL_SOURCE_ESEUTIL, hr, "Encountered Jet Error: 0x%08x", jEr); - - return hr; -} - -#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }} -#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; }} - -HRESULT DAPI EseBeginSession( - __out JET_INSTANCE *pjiInstance, - __out JET_SESID *pjsSession, - __in_z LPCWSTR pszInstance, - __in_z LPCWSTR pszPath - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiInstance = NULL; - LPSTR pszAnsiPath = NULL; - - hr = DirEnsureExists(pszPath, NULL); - EseExitOnFailure(hr, "Failed to ensure database directory exists"); - - // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, - // likely breaking everyone with unicode characters in their path. - hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting instance name to ansi"); - - hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting session path name to ansi"); - - jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance); - ExitOnJetFailure(jEr, hr, "Failed to create instance"); - - jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath); - ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath); - - // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution) - jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath); - ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath); - - jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter"); - - // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too) - - jEr = JetInit(pjiInstance); - ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance"); - - jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL); - ExitOnJetFailure(jEr, hr, "Failed to begin jet session"); - -LExit: - ReleaseStr(pszAnsiInstance); - ReleaseStr(pszAnsiPath); - - return hr; -} - -HRESULT DAPI EseEndSession( - __in JET_INSTANCE jiInstance, - __in JET_SESID jsSession - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetEndSession(jsSession, 0); - ExitOnJetFailure(jEr, hr, "Failed to end jet session"); - - jEr = JetTerm(jiInstance); - ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance"); - -LExit: - return hr; -} - -// Utility function used by EnsureSchema() -HRESULT AllocColumnCreateStruct( - __in const ESE_TABLE_SCHEMA *ptsSchema, - __deref_out JET_COLUMNCREATE **ppjccColumnCreate - ) -{ - HRESULT hr = S_OK; - DWORD_PTR i; - size_t cbAllocSize = 0; - - hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize)); - EseExitOnFailure(hr, "Maximum allocation exceeded."); - - *ppjccColumnCreate = static_cast(MemAlloc(cbAllocSize, TRUE)); - EseExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database"); - - for (i = 0; i < ptsSchema->dwColumns; ++i) - { - (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE); - - hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName); - - (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType; - - if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp) - { - (*ppjccColumnCreate)[i].cbMax = 256; - } - else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp) - { - (*ppjccColumnCreate)[i].cbMax = 2147483648; - (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged - ptsSchema->pcsColumns[i].fNullable = TRUE; - } - else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp) - { - (*ppjccColumnCreate)[i].cbMax = 4; - - if (ptsSchema->pcsColumns[i].fAutoIncrement) - { - (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement; - } - } - - if (!(ptsSchema->pcsColumns[i].fNullable)) - { - (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL; - } - - (*ppjccColumnCreate)[i].pvDefault = NULL; - (*ppjccColumnCreate)[i].cbDefault = 0; - (*ppjccColumnCreate)[i].cp = 1200; - (*ppjccColumnCreate)[i].columnid = 0; - (*ppjccColumnCreate)[i].err = 0; - } - -LExit: - return hr; -} - -HRESULT FreeColumnCreateStruct( - __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate, - __in DWORD dwColumns - ) -{ - HRESULT hr = S_OK; - DWORD i; - - for (i = 0; i < dwColumns; ++i) - { - ReleaseStr((pjccColumnCreate[i]).szColumnName); - } - - hr = MemFree(pjccColumnCreate); - EseExitOnFailure(hr, "Failed to release core column create struct"); - -LExit: - return hr; -} - -// Utility function used by EnsureSchema() -HRESULT AllocIndexCreateStruct( - __in const ESE_TABLE_SCHEMA *ptsSchema, - __deref_out JET_INDEXCREATE **ppjicIndexCreate - ) -{ - HRESULT hr = S_OK; - LPSTR pszMultiSzKeys = NULL; - LPSTR pszIndexName = NULL; - LPSTR pszTempString = NULL; - BOOL fKeyColumns = FALSE; - DWORD_PTR i; - - for (i=0; i < ptsSchema->dwColumns; ++i) - { - if (ptsSchema->pcsColumns[i].fKey) - { - hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName); - - hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0); - EseExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString); - - hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0); - EseExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString); - - ReleaseNullStr(pszTempString); - - // All question marks will be converted to null characters later; this is just to trick dutil - // into letting us create an ansi, double-null-terminated list of single-null-terminated strings - hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0); - EseExitOnFailure(hr, "Failed to append placeholder character to multisz string: %hs", pszMultiSzKeys); - - // Record that at least one key column was found - fKeyColumns = TRUE; - } - } - - // If no key columns were found, don't create an index - just return - if (!fKeyColumns) - { - ExitFunction1(hr = S_OK); - } - - hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName); - - hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0); - EseExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName); - - *ppjicIndexCreate = static_cast(MemAlloc(sizeof(JET_INDEXCREATE), TRUE)); - EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); - - // Record the size including both null terminators - the struct requires this - size_t cchSize = 0; - hr = ::StringCchLengthA(pszMultiSzKeys, STRSAFE_MAX_LENGTH, &cchSize); - EseExitOnRootFailure(hr, "Failed to get size of keys string"); - - ++cchSize; // add 1 to include null character at the end - - // At this point convert all question marks to null characters - for (i = 0; i < cchSize; ++i) - { - if ('?' == pszMultiSzKeys[i]) - { - pszMultiSzKeys[i] = '\0'; - } - } - - (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE); - (*ppjicIndexCreate)->szIndexName = pszIndexName; - (*ppjicIndexCreate)->szKey = pszMultiSzKeys; - (*ppjicIndexCreate)->cbKey = (DWORD)cchSize; - (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary; - (*ppjicIndexCreate)->ulDensity = 80; - (*ppjicIndexCreate)->lcid = 1033; - (*ppjicIndexCreate)->pidxunicode = NULL; - (*ppjicIndexCreate)->cbVarSegMac = 0; - (*ppjicIndexCreate)->rgconditionalcolumn = NULL; - (*ppjicIndexCreate)->cConditionalColumn = 0; - (*ppjicIndexCreate)->err = 0; - -LExit: - ReleaseStr(pszTempString); - - return hr; -} - -HRESULT EnsureSchema( - __in JET_DBID jdbDb, - __in JET_SESID jsSession, - __in ESE_DATABASE_SCHEMA *pdsSchema - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - BOOL fTransaction = FALSE; - DWORD dwTable; - DWORD dwColumn; - JET_TABLECREATE jtTableCreate = { }; - - // Set parameters which apply to all tables here - jtTableCreate.cbStruct = sizeof(jtTableCreate); - jtTableCreate.ulPages = 100; - jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value" - jtTableCreate.cIndexes = 1; - - hr = EseBeginTransaction(jsSession); - EseExitOnFailure(hr, "Failed to begin transaction to create tables"); - fTransaction = TRUE; - - for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable) - { - // Don't free this pointer - it's just a shortcut to the current table's name within the struct - LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName; - - // Ensure table exists - hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable); - if (E_NOTFOUND == hr) // if the table is missing, create it - { - // Fill out the JET_TABLECREATE struct - hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting table name to ansi"); - - hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate); - EseExitOnFailure(hr, "Failed to allocate column create struct"); - - hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate); - EseExitOnFailure(hr, "Failed to allocate index create struct"); - - jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns; - jtTableCreate.tableid = NULL; - - // TODO: Investigate why we can't create a table without a key column? - // Actually create the table using our JET_TABLECREATE struct - jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate); - ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName); - - // Record the table ID in our cache - pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid; - - // Record the column IDs in our cache - for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) - { - pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid; - } - - // Free and NULL things we allocated in this struct - ReleaseNullStr(jtTableCreate.szTableName); - - hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); - EseExitOnFailure(hr, "Failed to free column create struct"); - jtTableCreate.rgcolumncreate = NULL; - } - else - { - // If the table already exists, grab the column ids and put them into our cache - for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) - { - // Don't free this - it's just a shortcut to the current column within the struct - ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]); - ULONG ulColumnSize = 0; - BOOL fNullable = pcsColumn->fNullable; - - // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out! - if (JET_coltypText == pcsColumn->jcColumnType) - { - ulColumnSize = 256; - } - else if (JET_coltypLongText == pcsColumn->jcColumnType) - { - ulColumnSize = 2147483648; - fNullable = TRUE; - } - else if (JET_coltypLong == pcsColumn->jcColumnType) - { - ulColumnSize = 4; - fNullable = TRUE; - } - - hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn); - EseExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName); - } - } - } - -LExit: - ReleaseStr(jtTableCreate.szTableName); - - if (NULL != jtTableCreate.rgcolumncreate) - { - // Don't record the HRESULT here or it will override the return value of this function - FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); - } - - if (fTransaction) - { - EseCommitTransaction(jsSession); - } - - return hr; -} - -// Todo: support overwrite flag? Unfortunately, requires WinXP and up -// 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 -HRESULT DAPI EseEnsureDatabase( - __in JET_SESID jsSession, - __in_z LPCWSTR pszFile, - __in ESE_DATABASE_SCHEMA *pdsSchema, - __out JET_DBID* pjdbDb, - __in BOOL fExclusive, - __in BOOL fReadonly - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - JET_GRBIT jgrOptions = 0; - LPWSTR pszDir = NULL; - LPSTR pszAnsiFile = NULL; - - // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, - // likely breaking all those with unicode characters in their path. - hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting database name to ansi"); - - hr = PathGetDirectory(pszFile, &pszDir); - EseExitOnFailure(hr, "Failed to get directory that will contain database file"); - - hr = DirEnsureExists(pszDir, NULL); - EseExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir); - - if (FileExistsEx(pszFile, NULL)) - { - if (fReadonly) - { - jgrOptions = jgrOptions | JET_bitDbReadOnly; - } - - jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions); - ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile); - - // This flag doesn't apply to attach, only applies to Open, so only set it after the attach - if (fExclusive) - { - jgrOptions = jgrOptions | JET_bitDbExclusive; - } - - jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions); - ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile); - } - else - { - jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0); - ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile); - } - - hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema); - EseExitOnFailure(hr, "Failed to ensure database schema matches expectations"); - -LExit: - ReleaseStr(pszDir); - ReleaseStr(pszAnsiFile); - - return hr; -} - -HRESULT DAPI EseCloseDatabase( - __in JET_SESID jsSession, - __in JET_DBID jdbDb - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - JET_GRBIT jgrOptions = 0; - - jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions); - ExitOnJetFailure(jEr, hr, "Failed to close database"); - -LExit: - return hr; -} - -HRESULT DAPI EseCreateTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiTable = NULL; - - hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting table name to ansi"); - - jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable); - ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable); - -LExit: - ReleaseStr(pszAnsiTable); - - return hr; -} - -HRESULT DAPI EseOpenTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiTable = NULL; - - hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting table name to ansi"); - - jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable); - ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable); - -LExit: - ReleaseStr(pszAnsiTable); - - return hr; -} - -HRESULT DAPI EseCloseTable( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetCloseTable(jsSession, jtTable); - ExitOnJetFailure(jEr, hr, "Failed to close table"); - -LExit: - return hr; -} - -HRESULT DAPI EseEnsureColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __in JET_COLTYP jcColumnType, - __in ULONG ulColumnSize, - __in BOOL fFixed, - __in BOOL fNullable, - __out_opt JET_COLUMNID *pjcColumn - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiColumnName = NULL; - JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) }; - JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; - - hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting column name to ansi"); - - jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); - if (JET_errSuccess == jEr) - { - // Return the found columnID - if (NULL != pjcColumn) - { - *pjcColumn = jcdTempBase.columnid; - } - - ExitFunction1(hr = S_OK); - } - else if (JET_errColumnNotFound == jEr) - { - jEr = JET_errSuccess; - } - ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); - - jcdColumnDef.columnid = 0; - jcdColumnDef.coltyp = jcColumnType; - jcdColumnDef.wCountry = 0; - jcdColumnDef.langid = 0; - jcdColumnDef.cp = 1200; - jcdColumnDef.wCollate = 0; - jcdColumnDef.cbMax = ulColumnSize; - jcdColumnDef.grbit = 0; - - if (fFixed) - { - jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed; - } - if (!fNullable) - { - jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL; - } - - jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn); - ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName); - -LExit: - ReleaseStr(pszAnsiColumnName); - - return hr; -} - -HRESULT DAPI EseGetColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __out JET_COLUMNID *pjcColumn - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiColumnName = NULL; - JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; - - hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting column name to ansi"); - - jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); - if (JET_errSuccess == jEr) - { - // Return the found columnID - if (NULL != pjcColumn) - { - *pjcColumn = jcdTempBase.columnid; - } - - ExitFunction1(hr = S_OK); - } - ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); - -LExit: - ReleaseStr(pszAnsiColumnName); - - return hr; -} - -HRESULT DAPI EseMoveCursor( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in LONG lRow - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetMove(jsSession, jtTable, lRow, 0); - ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow); - -LExit: - return hr; -} - -HRESULT DAPI EseDeleteRow( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetDelete(jsSession, jtTable); - ExitOnJetFailure(jEr, hr, "Failed to delete row"); - -LExit: - return hr; -} - -HRESULT DAPI EseBeginTransaction( - __in JET_SESID jsSession - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetBeginTransaction(jsSession); - ExitOnJetFailure(jEr, hr, "Failed to begin transaction"); - -LExit: - return hr; -} - -HRESULT DAPI EseRollbackTransaction( - __in JET_SESID jsSession, - __in BOOL fAll - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0); - ExitOnJetFailure(jEr, hr, "Failed to rollback transaction"); - -LExit: - return hr; -} - -HRESULT DAPI EseCommitTransaction( - __in JET_SESID jsSession - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetCommitTransaction(jsSession, 0); - ExitOnJetFailure(jEr, hr, "Failed to commit transaction"); - -LExit: - return hr; -} - -HRESULT DAPI EsePrepareUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ULONG ulPrep - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep); - ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep); - -LExit: - return hr; -} - -HRESULT DAPI EseFinishUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in BOOL fSeekToInsertedRecord - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - unsigned char rgbBookmark[JET_cbBookmarkMost + 1]; - DWORD cbBookmark; - - if (fSeekToInsertedRecord) - { - jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark); - ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark"); - - jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark); - ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark"); - } - else - { - jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)"); - } - -LExit: - // 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 - if (FAILED(hr)) - { - JetPrepareUpdate(jsSession, jtTable, JET_prepCancel); - } - - return hr; -} - -HRESULT DAPI EseSetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast(cbBuffer), 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in BOOL fValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - BYTE bValue = fValue ? 0xFF : 0x00; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_z LPCWSTR pwzValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - size_t cchValue = 0; - ULONG cbValueSize = 0; - - if (pwzValue) - { - hr = ::StringCchLengthW(pwzValue, STRSAFE_MAX_LENGTH, &cchValue); - EseExitOnRootFailure(hr, "Failed to get string length: %ls", pwzValue); - } - - cbValueSize = static_cast((cchValue + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnEmpty( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database"); - -LExit: - return hr; -} - -HRESULT DAPI EseGetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - ULONG ulActualSize = 0; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); - if (JET_wrnBufferTruncated == jEr) - { - jEr = JET_errSuccess; - } - ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record"); - - if (NULL == *ppbBuffer) - { - *ppbBuffer = reinterpret_cast(MemAlloc(ulActualSize, FALSE)); - EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column"); - } - else - { - *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, ulActualSize, FALSE)); - EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column"); - } - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record"); - - *piBuffer = static_cast(ulActualSize); - -LExit: - if (FAILED(hr)) - { - ReleaseNullMem(*ppbBuffer); - } - - return hr; -} - -HRESULT DAPI EseGetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out DWORD *pdwValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record"); - -LExit: - return hr; -} - -HRESULT DAPI EseGetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out BOOL *pfValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - BYTE bValue = 0; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record"); - - if (bValue == 0) - { - *pfValue = FALSE; - } - else - { - *pfValue = TRUE; - } - -LExit: - return hr; -} - -HRESULT DAPI EseGetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out LPWSTR *ppszValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - ULONG ulActualSize = 0; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); - if (JET_wrnBufferTruncated == jEr) - { - jEr = JET_errSuccess; - } - ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record"); - - hr = StrAlloc(ppszValue, ulActualSize); - EseExitOnFailure(hr, "Failed to allocate string while retrieving column value"); - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record"); - -LExit: - return hr; -} - -HRESULT DAPI EseBeginQuery( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ESE_QUERY_TYPE qtQueryType, - __out ESE_QUERY_HANDLE *peqhHandle - ) -{ - UNREFERENCED_PARAMETER(jsSession); - UNREFERENCED_PARAMETER(jtTable); - - HRESULT hr = S_OK; - - *peqhHandle = static_cast(MemAlloc(sizeof(ESE_QUERY), TRUE)); - EseExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query"); - - ESE_QUERY *peqHandle = static_cast(*peqhHandle); - peqHandle->qtQueryType = qtQueryType; - peqHandle->jsSession = jsSession; - peqHandle->jtTable = jtTable; - -LExit: - return hr; -} - -// Utility function used by other functions to set a query column -HRESULT DAPI SetQueryColumn( - __in ESE_QUERY_HANDLE eqhHandle, - __in_bcount(cbData) const void *pvData, - __in DWORD cbData, - __in JET_GRBIT jGrb - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - ESE_QUERY *peqHandle = static_cast(eqhHandle); - - if (peqHandle->dwColumns == countof(peqHandle->pvData)) - { - hr = E_NOTIMPL; - EseExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData)); - } - - if (0 == peqHandle->dwColumns) // If it's the first column, start a new key - { - jGrb = jGrb | JET_bitNewKey; - } - - jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb); - ExitOnJetFailure(jEr, hr, "Failed to begin new query"); - - // If the query is wildcard, setup the cached copy of pvData - if (ESE_QUERY_EXACT != peqHandle->qtQueryType) - { - peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE); - EseExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory"); - - memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData); - - peqHandle->cbData[peqHandle->dwColumns] = cbData; - } - - // Increment the number of total columns - ++peqHandle->dwColumns; - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnBinary( - __in ESE_QUERY_HANDLE eqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ) -{ - HRESULT hr = S_OK; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (cbBuffer > DWORD_MAX) - { - ExitFunction1(hr = E_INVALIDARG); - } - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, reinterpret_cast(pbBuffer), static_cast(cbBuffer), jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as binary) to:"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnDword( - __in ESE_QUERY_HANDLE eqhHandle, - __in DWORD dwData, - __in BOOL fFinal - ) -{ - HRESULT hr = S_OK; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData); - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnBool( - __in ESE_QUERY_HANDLE eqhHandle, - __in BOOL fValue, - __in BOOL fFinal - ) -{ - HRESULT hr = S_OK; - BYTE bByte = fValue ? 0xFF : 0x00; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnString( - __in ESE_QUERY_HANDLE eqhHandle, - __in_z LPCWSTR pszString, - __in BOOL fFinal - ) -{ - HRESULT hr = S_OK; - DWORD dwStringSize = 0; - size_t cchString = 0; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (pszString) - { - hr = ::StringCchLengthW(pszString, STRSAFE_MAX_LENGTH, &cchString); - EseExitOnRootFailure(hr, "Failed to get size of column string"); - } - - dwStringSize = static_cast(sizeof(WCHAR) * (cchString + 1)); // Add 1 for null terminator - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString); - -LExit: - return hr; -} - -HRESULT DAPI EseFinishQuery( - __in ESE_QUERY_HANDLE eqhHandle - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - ESE_QUERY *peqHandle = static_cast(eqhHandle); - - if (peqHandle->fIndexRangeSet) - { - jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove); - ExitOnJetFailure(jEr, hr, "Failed to release index range"); - - peqHandle->fIndexRangeSet = FALSE; - } - - for (int i=0; i < countof(peqHandle->pvData); ++i) - { - ReleaseMem(peqHandle->pvData[i]); - } - - ReleaseMem(peqHandle); - -LExit: - return hr; -} - -HRESULT DAPI EseRunQuery( - __in ESE_QUERY_HANDLE eqhHandle - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - JET_GRBIT jGrb = 0; - JET_GRBIT jGrbSeekType = 0; - DWORD i; - - ESE_QUERY *peqHandle = static_cast(eqhHandle); - - if (ESE_QUERY_EXACT == peqHandle->qtQueryType) - { - jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ); - ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table"); - } - else - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrbSeekType = JET_bitSeekGE; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrbSeekType = JET_bitSeekLE; - } - - jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType); - if (jEr == JET_wrnSeekNotEqual) - { - jEr = JET_errSuccess; - } - - // At this point we've already set our cursor to the beginning of the range of records to select. - // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange() - // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx - for (i = 0; i < peqHandle->dwColumns; ++i) - { - if (i == 0) - { - jGrb = JET_bitNewKey; - } - else - { - jGrb = 0; - } - - // On the last iteration - if (i == peqHandle->dwColumns - 1) - { - jGrb |= JET_bitFullColumnEndLimit; - } - - jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb); - ExitOnJetFailure(jEr, hr, "Failed to begin new query"); - } - - jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit); - ExitOnJetFailure(jEr, hr, "Failed to set index range"); - - peqHandle->fIndexRangeSet = TRUE; - - // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does) - // So, let's check if there is a current record before returning (by reading the first byte of one). - jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0); - ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query"); - } - -LExit: - return hr; -} diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp deleted file mode 100644 index 1822727a..00000000 --- a/src/dutil/fileutil.cpp +++ /dev/null @@ -1,2032 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) -#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) -#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) -#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) -#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__) -#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__) - -// constants - -const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; -const BYTE UTF16BOM[] = {0xFF, 0xFE}; - -const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; -const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; - -/******************************************************************* - FileFromPath - returns a pointer to the file part of the path - -********************************************************************/ -extern "C" LPWSTR DAPI FileFromPath( - __in_z LPCWSTR wzPath - ) -{ - if (!wzPath) - return NULL; - - LPWSTR wzFile = const_cast(wzPath); - for (LPWSTR wz = wzFile; *wz; ++wz) - { - // valid delineators - // \ => Windows path - // / => unix and URL path - // : => relative path from mapped root - if (L'\\' == *wz || L'/' == *wz || L':' == *wz) - wzFile = wz + 1; - } - - return wzFile; -} - - -/******************************************************************* - FileResolvePath - gets the full path to a file resolving environment - variables along the way. - -********************************************************************/ -extern "C" HRESULT DAPI FileResolvePath( - __in_z LPCWSTR wzRelativePath, - __out LPWSTR *ppwzFullPath - ) -{ - Assert(wzRelativePath && *wzRelativePath); - - HRESULT hr = S_OK; - DWORD cch = 0; - LPWSTR pwzExpandedPath = NULL; - DWORD cchExpandedPath = 0; - - LPWSTR pwzFullPath = NULL; - DWORD cchFullPath = 0; - - LPWSTR wzFileName = NULL; - - // - // First, expand any environment variables. - // - cchExpandedPath = MAX_PATH; - hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); - FileExitOnFailure(hr, "Failed to allocate space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - cchExpandedPath = cch; - hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); - FileExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - FileExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); - } - } - - // - // Second, get the full path. - // - cchFullPath = MAX_PATH; - hr = StrAlloc(&pwzFullPath, cchFullPath); - FileExitOnFailure(hr, "Failed to allocate space for full path."); - - cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); - } - else if (cchFullPath < cch) - { - cchFullPath = cch; - hr = StrAlloc(&pwzFullPath, cchFullPath); - FileExitOnFailure(hr, "Failed to re-allocate more space for full path."); - - cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); - } - else if (cchFullPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - FileExitOnRootFailure(hr, "Failed to allocate buffer for full path."); - } - } - - *ppwzFullPath = pwzFullPath; - pwzFullPath = NULL; - -LExit: - ReleaseStr(pwzFullPath); - ReleaseStr(pwzExpandedPath); - - return hr; -} - - -/******************************************************************* -FileStripExtension - Strip extension from filename -********************************************************************/ -extern "C" HRESULT DAPI FileStripExtension( -__in_z LPCWSTR wzFileName, -__out LPWSTR *ppwzFileNameNoExtension -) -{ - Assert(wzFileName && *wzFileName); - - HRESULT hr = S_OK; - size_t cchFileName = 0; - LPWSTR pwzFileNameNoExtension = NULL; - size_t cchFileNameNoExtension = 0; - errno_t err = 0; - - hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_LENGTH, &cchFileName); - FileExitOnRootFailure(hr, "failed to get length of file name: %ls", wzFileName); - - cchFileNameNoExtension = cchFileName + 1; - - hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); - FileExitOnFailure(hr, "failed to allocate space for File Name without extension"); - - // _wsplitpath_s can handle drive/path/filename/extension - err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); - if (err) - { - hr = E_INVALIDARG; - FileExitOnRootFailure(hr, "failed to parse filename: '%ls', error: %d", wzFileName, err); - } - - *ppwzFileNameNoExtension = pwzFileNameNoExtension; - pwzFileNameNoExtension = NULL; - -LExit: - ReleaseStr(pwzFileNameNoExtension); - - return hr; -} - - -/******************************************************************* -FileChangeExtension - Changes the extension of a filename -********************************************************************/ -extern "C" HRESULT DAPI FileChangeExtension( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzNewExtension, - __out LPWSTR *ppwzFileNameNewExtension - ) -{ - Assert(wzFileName && *wzFileName); - - HRESULT hr = S_OK; - LPWSTR sczFileName = NULL; - - hr = FileStripExtension(wzFileName, &sczFileName); - FileExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); - - hr = StrAllocConcat(&sczFileName, wzNewExtension, 0); - FileExitOnFailure(hr, "Failed to add new extension."); - - *ppwzFileNameNewExtension = sczFileName; - sczFileName = NULL; - -LExit: - ReleaseStr(sczFileName); - - return hr; -} - - -/******************************************************************* -FileAddSuffixToBaseName - Adds a suffix the base portion of a file -name; e.g., file.ext to fileSuffix.ext. -********************************************************************/ -extern "C" HRESULT DAPI FileAddSuffixToBaseName( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzSuffix, - __out_z LPWSTR* psczNewFileName - ) -{ - Assert(wzFileName && *wzFileName); - - HRESULT hr = S_OK; - LPWSTR sczNewFileName = NULL; - size_t cchFileName = 0; - - hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_CCH, &cchFileName); - FileExitOnRootFailure(hr, "Failed to get length of file name: %ls", wzFileName); - - LPCWSTR wzExtension = wzFileName + cchFileName; - while (wzFileName < wzExtension && L'.' != *wzExtension) - { - --wzExtension; - } - - if (wzFileName < wzExtension) - { - // found an extension so add the suffix before it - hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension); - } - else - { - // no extension, so add the suffix at the end of the whole name - hr = StrAllocString(&sczNewFileName, wzFileName, 0); - FileExitOnFailure(hr, "Failed to allocate new file name."); - - hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0); - } - FileExitOnFailure(hr, "Failed to allocate new file name with suffix."); - - *psczNewFileName = sczNewFileName; - sczNewFileName = NULL; - -LExit: - ReleaseStr(sczNewFileName); - - return hr; -} - - -/******************************************************************* - FileVersion - -********************************************************************/ -extern "C" HRESULT DAPI FileVersion( - __in_z LPCWSTR wzFilename, - __out DWORD *pdwVerMajor, - __out DWORD* pdwVerMinor - ) -{ - HRESULT hr = S_OK; - - DWORD dwHandle = 0; - UINT cbVerBuffer = 0; - LPVOID pVerBuffer = NULL; - VS_FIXEDFILEINFO* pvsFileInfo = NULL; - UINT cbFileInfo = 0; - - if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle))) - { - FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); - } - - pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer); - FileExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); - - if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); - } - - if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); - } - - *pdwVerMajor = pvsFileInfo->dwFileVersionMS; - *pdwVerMinor = pvsFileInfo->dwFileVersionLS; - -LExit: - if (pVerBuffer) - { - ::GlobalFree(pVerBuffer); - } - return hr; -} - - -/******************************************************************* - FileVersionFromString - -*******************************************************************/ -extern "C" HRESULT DAPI FileVersionFromString( - __in_z LPCWSTR wzVersion, - __out DWORD* pdwVerMajor, - __out DWORD* pdwVerMinor - ) -{ - Assert(pdwVerMajor && pdwVerMinor); - - HRESULT hr = S_OK; - LPCWSTR pwz = wzVersion; - DWORD dw; - - *pdwVerMajor = 0; - *pdwVerMinor = 0; - - if ((L'v' == *pwz) || (L'V' == *pwz)) - { - ++pwz; - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) - { - *pdwVerMajor = dw << 16; - - if (!*pwz) - { - ExitFunction1(hr = S_OK); - } - ++pwz; - } - else - { - ExitFunction1(hr = S_FALSE); - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) - { - *pdwVerMajor |= dw; - - if (!*pwz) - { - ExitFunction1(hr = S_OK); - } - ++pwz; - } - else - { - ExitFunction1(hr = S_FALSE); - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) - { - *pdwVerMinor = dw << 16; - - if (!*pwz) - { - ExitFunction1(hr = S_OK); - } - ++pwz; - } - else - { - ExitFunction1(hr = S_FALSE); - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && L'\0' == *pwz && dw < 0x10000) - { - *pdwVerMinor |= dw; - } - else - { - ExitFunction1(hr = S_FALSE); - } - -LExit: - return hr; -} - - -/******************************************************************* - FileVersionFromStringEx - -*******************************************************************/ -extern "C" HRESULT DAPI FileVersionFromStringEx( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __out DWORD64* pqwVersion - ) -{ - Assert(wzVersion); - Assert(pqwVersion); - - HRESULT hr = S_OK; - LPCWSTR wzEnd = NULL; - LPCWSTR wzPartBegin = wzVersion; - LPCWSTR wzPartEnd = wzVersion; - DWORD iPart = 0; - USHORT us = 0; - DWORD64 qwVersion = 0; - - // get string length if not provided - if (0 >= cchVersion) - { - hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); - FileExitOnRootFailure(hr, "Failed to get length of file version string: %ls", wzVersion); - - if (0 >= cchVersion) - { - ExitFunction1(hr = E_INVALIDARG); - } - } - - if ((L'v' == *wzVersion) || (L'V' == *wzVersion)) - { - ++wzVersion; - --cchVersion; - wzPartBegin = wzVersion; - wzPartEnd = wzVersion; - } - - // save end pointer - wzEnd = wzVersion + cchVersion; - - // loop through parts - for (;;) - { - if (4 <= iPart) - { - // error, too many parts - ExitFunction1(hr = E_INVALIDARG); - } - - // find end of part - while (wzPartEnd < wzEnd && L'.' != *wzPartEnd) - { - ++wzPartEnd; - } - if (wzPartBegin == wzPartEnd) - { - // error, empty part - ExitFunction1(hr = E_INVALIDARG); - } - - DWORD cchPart; - hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); - FileExitOnFailure(hr, "Version number part was too long."); - - // parse version part - hr = StrStringToUInt16(wzPartBegin, cchPart, &us); - FileExitOnFailure(hr, "Failed to parse version number part."); - - // add part to qword version - qwVersion |= (DWORD64)us << ((3 - iPart) * 16); - - if (wzPartEnd >= wzEnd) - { - // end of string - break; - } - - wzPartBegin = ++wzPartEnd; // skip over separator - ++iPart; - } - - *pqwVersion = qwVersion; - -LExit: - return hr; -} - -/******************************************************************* - FileVersionFromStringEx - Formats the DWORD64 as a string version. - -*******************************************************************/ -extern "C" HRESULT DAPI FileVersionToStringEx( - __in DWORD64 qwVersion, - __out LPWSTR* psczVersion - ) -{ - HRESULT hr = S_OK; - WORD wMajor = 0; - WORD wMinor = 0; - WORD wBuild = 0; - WORD wRevision = 0; - - // Mask and shift each WORD for each field. - wMajor = (WORD)(qwVersion >> 48 & 0xffff); - wMinor = (WORD)(qwVersion >> 32 & 0xffff); - wBuild = (WORD)(qwVersion >> 16 & 0xffff); - wRevision = (WORD)(qwVersion & 0xffff); - - // Format and return the version string. - hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision); - FileExitOnFailure(hr, "Failed to allocate and format the version number."); - -LExit: - return hr; -} - -/******************************************************************* - FileSetPointer - sets the file pointer. - -********************************************************************/ -extern "C" HRESULT DAPI FileSetPointer( - __in HANDLE hFile, - __in DWORD64 dw64Move, - __out_opt DWORD64* pdw64NewPosition, - __in DWORD dwMoveMethod - ) -{ - Assert(INVALID_HANDLE_VALUE != hFile); - - HRESULT hr = S_OK; - LARGE_INTEGER liMove; - LARGE_INTEGER liNewPosition; - - liMove.QuadPart = dw64Move; - if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) - { - FileExitWithLastError(hr, "Failed to set file pointer."); - } - - if (pdw64NewPosition) - { - *pdw64NewPosition = liNewPosition.QuadPart; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileSize - -********************************************************************/ -extern "C" HRESULT DAPI FileSize( - __in_z LPCWSTR pwzFileName, - __out LONGLONG* pllSize - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - FileExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); - - hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (INVALID_HANDLE_VALUE == hFile) - { - FileExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); - } - - hr = FileSizeByHandle(hFile, pllSize); - FileExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); - -LExit: - ReleaseFileHandle(hFile); - - return hr; -} - - -/******************************************************************* - FileSizeByHandle - -********************************************************************/ -extern "C" HRESULT DAPI FileSizeByHandle( - __in HANDLE hFile, - __out LONGLONG* pllSize - ) -{ - Assert(INVALID_HANDLE_VALUE != hFile && pllSize); - HRESULT hr = S_OK; - LARGE_INTEGER li; - - *pllSize = 0; - - if (!::GetFileSizeEx(hFile, &li)) - { - FileExitWithLastError(hr, "Failed to get size of file."); - } - - *pllSize = li.QuadPart; - -LExit: - return hr; -} - - -/******************************************************************* - FileExistsEx - -********************************************************************/ -extern "C" BOOL DAPI FileExistsEx( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ) -{ - Assert(wzPath && *wzPath); - BOOL fExists = FALSE; - - WIN32_FIND_DATAW fd = { }; - HANDLE hff; - - if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd))) - { - ::FindClose(hff); - if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - if (pdwAttributes) - { - *pdwAttributes = fd.dwFileAttributes; - } - - fExists = TRUE; - } - } - - return fExists; -} - - -/******************************************************************* - FileExistsAfterRestart - checks that a file exists and will continue - to exist after restart. - -********************************************************************/ -extern "C" BOOL DAPI FileExistsAfterRestart( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ) -{ - HRESULT hr = S_OK; - BOOL fExists = FALSE; - HKEY hkPendingFileRename = NULL; - LPWSTR* rgsczRenames = NULL; - DWORD cRenames = 0; - int nCompare = 0; - - fExists = FileExistsEx(wzPath, pdwAttributes); - if (fExists) - { - hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to open pending file rename registry key."); - - hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to read pending file renames."); - - // The pending file renames array is pairs of source and target paths. We only care - // about checking the source paths so skip the target paths (i += 2). - for (DWORD i = 0; i < cRenames; i += 2) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename && *wzRename) - { - // Skip the long path designator if present. - if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) - { - wzRename += 4; - } - - hr = PathCompare(wzPath, wzRename, &nCompare); - FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); - - if (CSTR_EQUAL == nCompare) - { - fExists = FALSE; - break; - } - } - } - } - -LExit: - ReleaseStrArray(rgsczRenames, cRenames); - ReleaseRegKey(hkPendingFileRename); - - return fExists; -} - - -/******************************************************************* - FileRemoveFromPendingRename - removes the file path from the pending - file rename list. - -********************************************************************/ -extern "C" HRESULT DAPI FileRemoveFromPendingRename( - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - HKEY hkPendingFileRename = NULL; - LPWSTR* rgsczRenames = NULL; - DWORD cRenames = 0; - int nCompare = 0; - BOOL fRemoved = FALSE; - DWORD cNewRenames = 0; - - hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to open pending file rename registry key."); - - hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to read pending file renames."); - - // The pending file renames array is pairs of source and target paths. We only care - // about checking the source paths so skip the target paths (i += 2). - for (DWORD i = 0; i < cRenames; i += 2) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename && *wzRename) - { - // Skip the long path designator if present. - if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) - { - wzRename += 4; - } - - hr = PathCompare(wzPath, wzRename, &nCompare); - FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); - - // If we find our path in the list, null out the source and target slot and - // we'll compact the array next. - if (CSTR_EQUAL == nCompare) - { - ReleaseNullStr(rgsczRenames[i]); - ReleaseNullStr(rgsczRenames[i + 1]); - fRemoved = TRUE; - } - } - } - - if (fRemoved) - { - // Compact the array by removing any nulls. - for (DWORD i = 0; i < cRenames; ++i) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename) - { - rgsczRenames[cNewRenames] = wzRename; - ++cNewRenames; - } - } - - cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. - - // Write the new array back to the pending file rename key. - hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); - FileExitOnFailure(hr, "Failed to update pending file renames."); - } - -LExit: - ReleaseStrArray(rgsczRenames, cRenames); - ReleaseRegKey(hkPendingFileRename); - - return hr; -} - - -/******************************************************************* - FileRead - read a file into memory - -********************************************************************/ -extern "C" HRESULT DAPI FileRead( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath - ) -{ - HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE); - return hr; -} - -/******************************************************************* - FileRead - read a file into memory with specified share mode - -********************************************************************/ -extern "C" HRESULT DAPI FileReadEx( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD dwShareMode - ) -{ - HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode); - return hr; -} - -/******************************************************************* - FileReadUntil - read a file into memory with a maximum size - -********************************************************************/ -extern "C" HRESULT DAPI FileReadUntil( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD cbMaxRead - ) -{ - HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE); - return hr; -} - - -/******************************************************************* - FileReadPartial - read a portion of a file into memory - -********************************************************************/ -extern "C" HRESULT DAPI FileReadPartial( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK - ) -{ - return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE); -} - -/******************************************************************* - FileReadPartial - read a portion of a file into memory - (with specified share mode) -********************************************************************/ -extern "C" HRESULT DAPI FileReadPartialEx( - __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK, - __in DWORD dwShareMode - ) -{ - HRESULT hr = S_OK; - - UINT er = ERROR_SUCCESS; - HANDLE hFile = INVALID_HANDLE_VALUE; - LARGE_INTEGER liFileSize = { }; - DWORD cbData = 0; - BYTE* pbData = NULL; - - FileExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); - FileExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); - FileExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); - FileExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); - - hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - er = ::GetLastError(); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - FileExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); - } - - if (!::GetFileSizeEx(hFile, &liFileSize)) - { - FileExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); - } - - if (fSeek) - { - if (cbStartPosition > liFileSize.QuadPart) - { - hr = E_INVALIDARG; - FileExitOnFailure(hr, "Start position %d bigger than file '%ls' size %llu", cbStartPosition, wzSrcPath, liFileSize.QuadPart); - } - - DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT); - if (INVALID_SET_FILE_POINTER == dwErr) - { - FileExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); - } - } - else - { - cbStartPosition = 0; - } - - if (fPartialOK) - { - cbData = cbMaxRead; - } - else - { - cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD - if (cbMaxRead < liFileSize.QuadPart - cbStartPosition) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - FileExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); - } - } - - if (*ppbDest) - { - if (0 == cbData) - { - ReleaseNullMem(*ppbDest); - *pcbDest = 0; - ExitFunction1(hr = S_OK); - } - - LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE); - FileExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); - - pbData = static_cast(pv); - } - else - { - if (0 == cbData) - { - *pcbDest = 0; - ExitFunction1(hr = S_OK); - } - - pbData = static_cast(MemAlloc(cbData, TRUE)); - FileExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); - } - - DWORD cbTotalRead = 0; - DWORD cbRead = 0; - do - { - DWORD cbRemaining = 0; - hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining); - FileExitOnFailure(hr, "Underflow calculating remaining buffer size."); - - if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); - } - - cbTotalRead += cbRead; - } while (cbRead); - - if (cbTotalRead != cbData) - { - hr = E_UNEXPECTED; - FileExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); - } - - *ppbDest = pbData; - pbData = NULL; - *pcbDest = cbData; - -LExit: - ReleaseMem(pbData); - ReleaseFile(hFile); - - return hr; -} - -extern "C" HRESULT DAPI FileReadHandle( - __in HANDLE hFile, - __in_bcount(cbDest) LPBYTE pbDest, - __in SIZE_T cbDest - ) -{ - HRESULT hr = 0; - DWORD cbDataRead = 0; - SIZE_T cbRemaining = cbDest; - SIZE_T cbTotal = 0; - - while (0 < cbRemaining) - { - if (!::ReadFile(hFile, pbDest + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataRead, NULL)) - { - DWORD er = ::GetLastError(); - if (ERROR_MORE_DATA == er) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - FileExitOnRootFailure(hr, "Failed to read data from file handle."); - } - - cbRemaining -= cbDataRead; - cbTotal += cbDataRead; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileWrite - write a file from memory - -********************************************************************/ -extern "C" HRESULT DAPI FileWrite( - __in_z LPCWSTR pwzFileName, - __in DWORD dwFlagsAndAttributes, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData, - __out_opt HANDLE* pHandle - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // Open the file - hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); - - hr = FileWriteHandle(hFile, pbData, cbData); - FileExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); - - if (pHandle) - { - *pHandle = hFile; - hFile = INVALID_HANDLE_VALUE; - } - -LExit: - ReleaseFile(hFile); - - return hr; -} - - -/******************************************************************* - FileWriteHandle - write to a file handle from memory - -********************************************************************/ -extern "C" HRESULT DAPI FileWriteHandle( - __in HANDLE hFile, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - DWORD cbDataWritten = 0; - SIZE_T cbTotal = 0; - SIZE_T cbRemaining = cbData; - - // Write out all of the data. - while (0 < cbRemaining) - { - if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataWritten, NULL)) - { - FileExitOnLastError(hr, "Failed to write data to file handle."); - } - - cbRemaining -= cbDataWritten; - cbTotal += cbDataWritten; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileCopyUsingHandles - -*******************************************************************/ -extern "C" HRESULT DAPI FileCopyUsingHandles( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __out_opt DWORD64* pcbCopied - ) -{ - HRESULT hr = S_OK; - DWORD64 cbTotalCopied = 0; - BYTE rgbData[4 * 1024]; - DWORD cbRead = 0; - - do - { - cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); - if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read from source."); - } - - if (cbRead) - { - hr = FileWriteHandle(hTarget, rgbData, cbRead); - FileExitOnFailure(hr, "Failed to write to target."); - } - - cbTotalCopied += cbRead; - } while (cbTotalCopied < cbCopy && 0 != cbRead); - - if (pcbCopied) - { - *pcbCopied = cbTotalCopied; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileCopyUsingHandlesWithProgress - -*******************************************************************/ -extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - __in_opt LPVOID lpData - ) -{ - HRESULT hr = S_OK; - DWORD64 cbTotalCopied = 0; - BYTE rgbData[64 * 1024]; - DWORD cbRead = 0; - - LARGE_INTEGER liSourceSize = { }; - LARGE_INTEGER liTotalCopied = { }; - LARGE_INTEGER liZero = { }; - DWORD dwResult = 0; - - hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart); - FileExitOnFailure(hr, "Failed to get size of source."); - - if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart) - { - liSourceSize.QuadPart = cbCopy; - } - - if (lpProgressRoutine) - { - dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData); - switch (dwResult) - { - case PROGRESS_CONTINUE: - break; - - case PROGRESS_CANCEL: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_STOP: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_QUIET: - lpProgressRoutine = NULL; - break; - } - } - - // Set size of the target file. - ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN); - - if (!::SetEndOfFile(hTarget)) - { - FileExitWithLastError(hr, "Failed to set end of target file."); - } - - if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN)) - { - FileExitWithLastError(hr, "Failed to reset target file pointer."); - } - - // Copy with progress. - while (0 == cbCopy || cbTotalCopied < cbCopy) - { - cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); - if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read from source."); - } - - if (cbRead) - { - hr = FileWriteHandle(hTarget, rgbData, cbRead); - FileExitOnFailure(hr, "Failed to write to target."); - - cbTotalCopied += cbRead; - - if (lpProgressRoutine) - { - liTotalCopied.QuadPart = cbTotalCopied; - dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData); - switch (dwResult) - { - case PROGRESS_CONTINUE: - break; - - case PROGRESS_CANCEL: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_STOP: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_QUIET: - lpProgressRoutine = NULL; - break; - } - } - } - else - { - break; - } - } - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureCopy - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureCopy( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite - ) -{ - HRESULT hr = S_OK; - DWORD er; - - // try to copy the file first - if (::CopyFileW(wzSource, wzTarget, !fOverwrite)) - { - ExitFunction(); // we're done - } - - er = ::GetLastError(); // check the error and do the right thing below - if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) - { - // if not overwriting this is an expected error - ExitFunction1(hr = S_FALSE); - } - else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist - { - // try to create the directory then do the copy - LPWSTR pwzLastSlash = NULL; - for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) - { - if (*pwz == L'\\') - { - pwzLastSlash = pwz; - } - } - - if (pwzLastSlash) - { - *pwzLastSlash = L'\0'; // null terminate - hr = DirEnsureExists(wzTarget, NULL); - *pwzLastSlash = L'\\'; // now put the slash back - FileExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); - - // try to copy again - if (!::CopyFileW(wzSource, wzTarget, fOverwrite)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); - } - } - else // no path was specified so just return the error - { - hr = HRESULT_FROM_WIN32(er); - } - } - else // unexpected error - { - hr = HRESULT_FROM_WIN32(er); - } - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureCopyWithRetry - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureCopyWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ) -{ - AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); - - HRESULT hr = E_FAIL; - DWORD i = 0; - - for (i = 0; FAILED(hr) && i <= cRetry; ++i) - { - if (0 < i) - { - ::Sleep(dwWaitMilliseconds); - } - - hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite); - if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr - || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr) - { - break; // no reason to retry these errors. - } - } - FileExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureMove - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureMove( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy - ) -{ - HRESULT hr = S_OK; - DWORD er; - - DWORD dwFlags = 0; - - if (fOverwrite) - { - dwFlags |= MOVEFILE_REPLACE_EXISTING; - } - if (fAllowCopy) - { - dwFlags |= MOVEFILE_COPY_ALLOWED; - } - - // try to move the file first - if (::MoveFileExW(wzSource, wzTarget, dwFlags)) - { - ExitFunction(); // we're done - } - - er = ::GetLastError(); // check the error and do the right thing below - if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) - { - // if not overwriting this is an expected error - ExitFunction1(hr = S_FALSE); - } - else if (ERROR_FILE_NOT_FOUND == er) - { - // We are seeing some cases where ::MoveFileEx() says a file was not found - // but the source file is actually present. In that case, return path not - // found so we try to create the target path since that is most likely - // what is missing. Otherwise, the source file is missing and we're obviously - // not going to be recovering from that. - if (FileExistsEx(wzSource, NULL)) - { - er = ERROR_PATH_NOT_FOUND; - } - } - - // If the path doesn't exist, try to create the directory tree then do the move. - if (ERROR_PATH_NOT_FOUND == er) - { - LPWSTR pwzLastSlash = NULL; - for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) - { - if (*pwz == L'\\') - { - pwzLastSlash = pwz; - } - } - - if (pwzLastSlash) - { - *pwzLastSlash = L'\0'; // null terminate - hr = DirEnsureExists(wzTarget, NULL); - *pwzLastSlash = L'\\'; // now put the slash back - FileExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); - - // try to move again - if (!::MoveFileExW(wzSource, wzTarget, dwFlags)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); - } - } - else // no path was specified so just return the error - { - hr = HRESULT_FROM_WIN32(er); - } - } - else // unexpected error - { - hr = HRESULT_FROM_WIN32(er); - } - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureMoveWithRetry - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureMoveWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ) -{ - AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); - - HRESULT hr = E_FAIL; - DWORD i = 0; - - for (i = 0; FAILED(hr) && i < cRetry + 1; ++i) - { - if (0 < i) - { - ::Sleep(dwWaitMilliseconds); - } - - hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy); - } - FileExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); - -LExit: - return hr; -} - - -/******************************************************************* - FileCreateTemp - creates an empty temp file - - NOTE: uses ANSI functions internally so it is Win9x safe -********************************************************************/ -extern "C" HRESULT DAPI FileCreateTemp( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ) -{ - Assert(wzPrefix && *wzPrefix); - HRESULT hr = S_OK; - LPSTR pszTempPath = NULL; - DWORD cchTempPath = MAX_PATH; - - HANDLE hTempFile = INVALID_HANDLE_VALUE; - LPSTR pszTempFile = NULL; - - int i = 0; - - hr = StrAnsiAlloc(&pszTempPath, cchTempPath); - FileExitOnFailure(hr, "failed to allocate memory for the temp path"); - ::GetTempPathA(cchTempPath, pszTempPath); - - for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) - { - hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension); - FileExitOnFailure(hr, "failed to allocate memory for log file"); - - hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // if the file already exists, just try again - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) - { - hr = S_OK; - continue; - } - FileExitOnFailureDebugTrace(hr, "failed to create file: %hs", pszTempFile); - } - } - - if (ppwzTempFile) - { - hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8); - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - -LExit: - ReleaseFile(hTempFile); - ReleaseStr(pszTempFile); - ReleaseStr(pszTempPath); - - return hr; -} - - -/******************************************************************* - FileCreateTempW - creates an empty temp file - -*******************************************************************/ -extern "C" HRESULT DAPI FileCreateTempW( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ) -{ - Assert(wzPrefix && *wzPrefix); - HRESULT hr = E_FAIL; - - WCHAR wzTempPath[MAX_PATH]; - DWORD cchTempPath = countof(wzTempPath); - LPWSTR pwzTempFile = NULL; - - HANDLE hTempFile = INVALID_HANDLE_VALUE; - int i = 0; - - if (!::GetTempPathW(cchTempPath, wzTempPath)) - { - FileExitOnLastError(hr, "failed to get temp path"); - } - - for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) - { - hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); - FileExitOnFailure(hr, "failed to allocate memory for temp filename"); - - hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // if the file already exists, just try again - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) - { - hr = S_OK; - continue; - } - FileExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); - } - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - - if (ppwzTempFile) - { - *ppwzTempFile = pwzTempFile; - pwzTempFile = NULL; - } - -LExit: - ReleaseFile(hTempFile); - ReleaseStr(pwzTempFile); - - return hr; -} - - -/******************************************************************* - FileIsSame - -********************************************************************/ -extern "C" HRESULT DAPI FileIsSame( - __in_z LPCWSTR wzFile1, - __in_z LPCWSTR wzFile2, - __out LPBOOL lpfSameFile - ) -{ - HRESULT hr = S_OK; - HANDLE hFile1 = NULL; - HANDLE hFile2 = NULL; - BY_HANDLE_FILE_INFORMATION fileInfo1 = { }; - BY_HANDLE_FILE_INFORMATION fileInfo2 = { }; - - hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); - - hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); - - if (!::GetFileInformationByHandle(hFile1, &fileInfo1)) - { - FileExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); - } - - if (!::GetFileInformationByHandle(hFile2, &fileInfo2)) - { - FileExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); - } - - *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber && - fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh && - fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE; - -LExit: - ReleaseFile(hFile1); - ReleaseFile(hFile2); - - return hr; -} - -/******************************************************************* - FileEnsureDelete - deletes a file, first removing read-only, - hidden, or system attributes if necessary. -********************************************************************/ -extern "C" HRESULT DAPI FileEnsureDelete( - __in_z LPCWSTR wzFile - ) -{ - HRESULT hr = S_OK; - - DWORD dwAttrib = INVALID_FILE_ATTRIBUTES; - if (FileExistsEx(wzFile, &dwAttrib)) - { - if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM) - { - if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL)) - { - FileExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); - } - } - - if (!::DeleteFileW(wzFile)) - { - FileExitOnLastError(hr, "Failed to delete file: %ls", wzFile); - } - } - -LExit: - return hr; -} - -/******************************************************************* - FileGetTime - Gets the file time of a specified file -********************************************************************/ -extern "C" HRESULT DAPI FileGetTime( - __in_z LPCWSTR wzFile, - __out_opt LPFILETIME lpCreationTime, - __out_opt LPFILETIME lpLastAccessTime, - __out_opt LPFILETIME lpLastWriteTime - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = NULL; - - hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); - - if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) - { - FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); - } - -LExit: - ReleaseFile(hFile); - return hr; -} - -/******************************************************************* - FileSetTime - Sets the file time of a specified file -********************************************************************/ -extern "C" HRESULT DAPI FileSetTime( - __in_z LPCWSTR wzFile, - __in_opt const FILETIME *lpCreationTime, - __in_opt const FILETIME *lpLastAccessTime, - __in_opt const FILETIME *lpLastWriteTime - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = NULL; - - hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); - - if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) - { - FileExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); - } - -LExit: - ReleaseFile(hFile); - return hr; -} - -/******************************************************************* - FileReSetTime - ReSets a file's last acess and modified time to the - creation time of the file -********************************************************************/ -extern "C" HRESULT DAPI FileResetTime( - __in_z LPCWSTR wzFile - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = NULL; - FILETIME ftCreateTime; - - hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); - - if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL)) - { - FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); - } - - if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime)) - { - FileExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); - } - -LExit: - ReleaseFile(hFile); - return hr; -} - - -/******************************************************************* - FileExecutableArchitecture - -*******************************************************************/ -extern "C" HRESULT DAPI FileExecutableArchitecture( - __in_z LPCWSTR wzFile, - __out FILE_ARCHITECTURE *pArchitecture - ) -{ - HRESULT hr = S_OK; - - HANDLE hFile = INVALID_HANDLE_VALUE; - DWORD cbRead = 0; - IMAGE_DOS_HEADER DosImageHeader = { }; - IMAGE_NT_HEADERS NtImageHeader = { }; - - hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (hFile == INVALID_HANDLE_VALUE) - { - FileExitWithLastError(hr, "Failed to open file: %ls", wzFile); - } - - if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); - } - - if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - FileExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); - } - - if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN)) - { - FileExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); - } - - if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); - } - - if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - FileExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); - } - - if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem || - IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem || - IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem) - { - switch (NtImageHeader.FileHeader.Machine) - { - case IMAGE_FILE_MACHINE_I386: - *pArchitecture = FILE_ARCHITECTURE_X86; - break; - case IMAGE_FILE_MACHINE_IA64: - *pArchitecture = FILE_ARCHITECTURE_IA64; - break; - case IMAGE_FILE_MACHINE_AMD64: - *pArchitecture = FILE_ARCHITECTURE_X64; - break; - default: - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - break; - } - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - } - FileExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); - -LExit: - if (hFile != INVALID_HANDLE_VALUE) - { - ::CloseHandle(hFile); - } - - return hr; -} - -/******************************************************************* - FileToString - -*******************************************************************/ -extern "C" HRESULT DAPI FileToString( - __in_z LPCWSTR wzFile, - __out LPWSTR *psczString, - __out_opt FILE_ENCODING *pfeEncoding - ) -{ - HRESULT hr = S_OK; - BYTE *pbFullFileBuffer = NULL; - SIZE_T cbFullFileBuffer = 0; - BOOL fNullCharFound = FALSE; - LPWSTR sczFileText = NULL; - - // Check if the file is ANSI - hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile); - FileExitOnFailure(hr, "Failed to read file: %ls", wzFile); - - if (0 == cbFullFileBuffer) - { - *psczString = NULL; - ExitFunction1(hr = S_OK); - } - - // UTF-8 BOM - if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM))) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM; - } - - hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8); - FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); - - *psczString = sczFileText; - sczFileText = NULL; - } - // UTF-16 BOM, little endian (windows regular UTF-16) - else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM))) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM; - } - - hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR)); - FileExitOnFailure(hr, "Failed to allocate copy of string"); - } - // No BOM, let's try to detect - else - { - for (DWORD i = 0; i < cbFullFileBuffer; ++i) - { - if (pbFullFileBuffer[i] == '\0') - { - fNullCharFound = TRUE; - break; - } - } - - if (!fNullCharFound) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF8; - } - - hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8); - if (FAILED(hr)) - { - if (E_OUTOFMEMORY == hr) - { - FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); - } - } - else - { - *psczString = sczFileText; - sczFileText = NULL; - } - } - else if (NULL == *psczString) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF16; - } - - hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR)); - FileExitOnFailure(hr, "Failed to allocate copy of string"); - } - } - -LExit: - ReleaseStr(sczFileText); - ReleaseMem(pbFullFileBuffer); - - return hr; -} - -/******************************************************************* - FileFromString - -*******************************************************************/ -extern "C" HRESULT DAPI FileFromString( - __in_z LPCWSTR wzFile, - __in DWORD dwFlagsAndAttributes, - __in_z LPCWSTR sczString, - __in FILE_ENCODING feEncoding - ) -{ - HRESULT hr = S_OK; - LPSTR sczUtf8String = NULL; - BYTE *pbFullFileBuffer = NULL; - const BYTE *pcbFullFileBuffer = NULL; - SIZE_T cbFullFileBuffer = 0; - SIZE_T cbStrLen = 0; - - switch (feEncoding) - { - case FILE_ENCODING_UTF8: - hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); - FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); - - hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbFullFileBuffer)); - FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); - - pcbFullFileBuffer = reinterpret_cast(sczUtf8String); - break; - case FILE_ENCODING_UTF8_WITH_BOM: - hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); - FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); - - hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); - FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); - - cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; - - pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); - FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); - - memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM)); - memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen); - pcbFullFileBuffer = pbFullFileBuffer; - break; - case FILE_ENCODING_UTF16: - hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); - FileExitOnRootFailure(hr, "Failed to get length of string"); - - cbFullFileBuffer = cbStrLen * sizeof(WCHAR); - pcbFullFileBuffer = reinterpret_cast(sczString); - break; - case FILE_ENCODING_UTF16_WITH_BOM: - hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); - FileExitOnRootFailure(hr, "Failed to get length of string"); - - cbStrLen *= sizeof(WCHAR); - cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; - - pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); - FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); - - memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM)); - memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen); - pcbFullFileBuffer = pbFullFileBuffer; - break; - } - - hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL); - FileExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); - -LExit: - ReleaseStr(sczUtf8String); - ReleaseMem(pbFullFileBuffer); - - return hr; -} diff --git a/src/dutil/gdiputil.cpp b/src/dutil/gdiputil.cpp deleted file mode 100644 index b5a0087c..00000000 --- a/src/dutil/gdiputil.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace Gdiplus; - - -// Exit macros -#define GdipExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) -#define GdipExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) -#define GdipExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) -#define GdipExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) -#define GdipExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GDIPUTIL, e, x, s, __VA_ARGS__) -#define GdipExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GDIPUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** - GdipInitialize - initializes GDI+. - - Note: pOutput must be non-NULL if pInput->SuppressBackgroundThread - is TRUE. See GdiplusStartup() for more information. -********************************************************************/ -extern "C" HRESULT DAPI GdipInitialize( - __in const Gdiplus::GdiplusStartupInput* pInput, - __out ULONG_PTR* pToken, - __out_opt Gdiplus::GdiplusStartupOutput *pOutput - ) -{ - AssertSz(!pInput->SuppressBackgroundThread || pOutput, "pOutput required if background thread suppressed."); - - HRESULT hr = S_OK; - Status status = Ok; - - status = GdiplusStartup(pToken, pInput, pOutput); - GdipExitOnGdipFailure(status, hr, "Failed to initialize GDI+."); - -LExit: - return hr; -} - -/******************************************************************** - GdipUninitialize - uninitializes GDI+. - -********************************************************************/ -extern "C" void DAPI GdipUninitialize( - __in ULONG_PTR token - ) -{ - GdiplusShutdown(token); -} - -/******************************************************************** - GdipBitmapFromResource - read a GDI+ image out of a resource stream - -********************************************************************/ -extern "C" HRESULT DAPI GdipBitmapFromResource( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szId, - __out Bitmap **ppBitmap - ) -{ - HRESULT hr = S_OK; - LPVOID pvData = NULL; - DWORD cbData = 0; - HGLOBAL hGlobal = NULL;; - LPVOID pv = NULL; - IStream *pStream = NULL; - Bitmap *pBitmap = NULL; - Status gs = Ok; - - hr = ResReadData(hinst, szId, &pvData, &cbData); - GdipExitOnFailure(hr, "Failed to load GDI+ bitmap from resource."); - - // Have to copy the fixed resource data into moveable (heap) memory - // since that's what GDI+ expects. - hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData); - GdipExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory."); - - pv = ::GlobalLock(hGlobal); - GdipExitOnNullWithLastError(pv, hr, "Failed to lock global memory."); - - memcpy(pv, pvData, cbData); - - ::GlobalUnlock(pv); // no point taking any more memory than we have already - pv = NULL; - - hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); - GdipExitOnFailure(hr, "Failed to allocate stream from global memory."); - - hGlobal = NULL; // we gave the global memory to the stream object so it will close it - - pBitmap = Bitmap::FromStream(pStream); - GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream."); - - gs = pBitmap->GetLastStatus(); - GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream."); - - *ppBitmap = pBitmap; - pBitmap = NULL; - -LExit: - if (pBitmap) - { - delete pBitmap; - } - - ReleaseObject(pStream); - - if (pv) - { - ::GlobalUnlock(pv); - } - - if (hGlobal) - { - ::GlobalFree(hGlobal); - } - - return hr; -} - - -/******************************************************************** - GdipBitmapFromFile - read a GDI+ image from a file. - -********************************************************************/ -extern "C" HRESULT DAPI GdipBitmapFromFile( - __in_z LPCWSTR wzFileName, - __out Bitmap **ppBitmap - ) -{ - HRESULT hr = S_OK; - Bitmap *pBitmap = NULL; - Status gs = Ok; - - GdipExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName"); - - pBitmap = Bitmap::FromFile(wzFileName); - GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file."); - - gs = pBitmap->GetLastStatus(); - GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName); - - *ppBitmap = pBitmap; - pBitmap = NULL; - -LExit: - if (pBitmap) - { - delete pBitmap; - } - - return hr; -} - - -HRESULT DAPI GdipHresultFromStatus( - __in Gdiplus::Status gs - ) -{ - switch (gs) - { - case Ok: - return S_OK; - - case GenericError: - return E_FAIL; - - case InvalidParameter: - return E_INVALIDARG; - - case OutOfMemory: - return E_OUTOFMEMORY; - - case ObjectBusy: - return HRESULT_FROM_WIN32(ERROR_BUSY); - - case InsufficientBuffer: - return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - - case NotImplemented: - return E_NOTIMPL; - - case Win32Error: - return E_FAIL; - - case WrongState: - return E_FAIL; - - case Aborted: - return E_ABORT; - - case FileNotFound: - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - - case ValueOverflow: - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - case AccessDenied: - return E_ACCESSDENIED; - - case UnknownImageFormat: - return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - - case FontFamilyNotFound: __fallthrough; - case FontStyleNotFound: __fallthrough; - case NotTrueTypeFont: - return E_UNEXPECTED; - - case UnsupportedGdiplusVersion: - return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - - case GdiplusNotInitialized: - return E_UNEXPECTED; - - case PropertyNotFound: __fallthrough; - case PropertyNotSupported: - return E_FAIL; - } - - return E_UNEXPECTED; -} diff --git a/src/dutil/guidutil.cpp b/src/dutil/guidutil.cpp deleted file mode 100644 index 204c9af2..00000000 --- a/src/dutil/guidutil.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define GuidExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) -#define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) -#define GuidExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) -#define GuidExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) -#define GuidExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GUIDUTIL, e, x, s, __VA_ARGS__) -#define GuidExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GUIDUTIL, g, x, s, __VA_ARGS__) - -extern "C" HRESULT DAPI GuidFixedCreate( - _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid - ) -{ - HRESULT hr = S_OK; - UUID guid = { }; - - hr = HRESULT_FROM_RPC(::UuidCreate(&guid)); - GuidExitOnFailure(hr, "UuidCreate failed."); - - if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) - { - hr = E_OUTOFMEMORY; - GuidExitOnRootFailure(hr, "Failed to convert guid into string."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI GuidCreate( - __deref_out_z LPWSTR* psczGuid - ) -{ - HRESULT hr = S_OK; - - hr = StrAlloc(psczGuid, GUID_STRING_LENGTH); - GuidExitOnFailure(hr, "Failed to allocate space for guid"); - - hr = GuidFixedCreate(*psczGuid); - GuidExitOnFailure(hr, "Failed to create new guid."); - -LExit: - return hr; -} diff --git a/src/dutil/iis7util.cpp b/src/dutil/iis7util.cpp deleted file mode 100644 index d0a0b000..00000000 --- a/src/dutil/iis7util.cpp +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#include "iis7util.h" - - -// Exit macros -#define IisExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) -#define IisExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) -#define IisExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) -#define IisExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) -#define IisExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_IIS7UTIL, e, x, s, __VA_ARGS__) -#define IisExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_IIS7UTIL, g, x, s, __VA_ARGS__) - -#define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt) - -extern "C" HRESULT DAPI Iis7PutPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT vtPut - ) -{ - HRESULT hr = S_OK; - IAppHostProperty *pProperty = NULL; - BSTR bstrPropName = NULL; - - bstrPropName = ::SysAllocString(wzPropName); - IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = pElement->GetPropertyByName(bstrPropName, &pProperty); - IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); - - hr = pProperty->put_Value(vtPut); - IisExitOnFailure(hr, "Failed to set property value for %ls", wzPropName); - -LExit: - ReleaseBSTR(bstrPropName); - // caller responsible for cleaning up variant vtPut - ReleaseObject(pProperty); - - return hr; -} - -extern "C" HRESULT DAPI Iis7PutPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPCWSTR wzString - ) -{ - HRESULT hr = S_OK; - VARIANT vtPut; - - ::VariantInit(&vtPut); - vtPut.vt = VT_BSTR; - vtPut.bstrVal = ::SysAllocString(wzString); - IisExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut); - -LExit: - ReleaseVariant(vtPut); - - return hr; -} - -extern "C" HRESULT DAPI Iis7PutPropertyInteger( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in DWORD dValue - ) -{ - VARIANT vtPut; - - ::VariantInit(&vtPut); - vtPut.vt = VT_I4; - vtPut.lVal = dValue; - return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); -} - -extern "C" HRESULT DAPI Iis7PutPropertyBool( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in BOOL fValue) -{ - VARIANT vtPut; - - ::VariantInit(&vtPut); - vtPut.vt = VT_BOOL; - vtPut.boolVal = (fValue == FALSE) ? VARIANT_FALSE : VARIANT_TRUE; - return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); -} - -extern "C" HRESULT DAPI Iis7GetPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT* vtGet - ) -{ - HRESULT hr = S_OK; - IAppHostProperty *pProperty = NULL; - BSTR bstrPropName = NULL; - - bstrPropName = ::SysAllocString(wzPropName); - IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = pElement->GetPropertyByName(bstrPropName, &pProperty); - IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); - - hr = pProperty->get_Value(vtGet); - IisExitOnFailure(hr, "Failed to get property value for %ls", wzPropName); - -LExit: - ReleaseBSTR(bstrPropName); - // caller responsible for cleaning up variant vtGet - ReleaseObject(pProperty); - - return hr; -} - -extern "C" HRESULT DAPI Iis7GetPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPWSTR* psczGet - ) -{ - HRESULT hr = S_OK; - VARIANT vtGet; - - ::VariantInit(&vtGet); - hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet); - IisExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName); - - if (!ISSTRINGVARIANT(vtGet.vt)) - { - hr = E_UNEXPECTED; - IisExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt); - } - - hr = StrAllocString(psczGet, vtGet.bstrVal, 0); - -LExit: - ReleaseVariant(vtGet); - - return hr; -} - -BOOL DAPI CompareVariantDefault( - __in VARIANT* pVariant1, - __in VARIANT* pVariant2 - ) -{ - BOOL fEqual = FALSE; - - switch (pVariant1->vt) - { - // VarCmp doesn't work for unsigned ints - // We'd like to allow signed/unsigned comparison as well since - // IIS doesn't document variant type for integer fields - case VT_I1: - case VT_UI1: - if (VT_I1 == pVariant2->vt || VT_UI1 == pVariant2->vt) - { - fEqual = pVariant1->bVal == pVariant2->bVal; - } - break; - case VT_I2: - case VT_UI2: - if (VT_I2 == pVariant2->vt || VT_UI2 == pVariant2->vt) - { - fEqual = pVariant1->uiVal == pVariant2->uiVal; - } - break; - case VT_UI4: - case VT_I4: - if (VT_I4 == pVariant2->vt || VT_UI4 == pVariant2->vt) - { - fEqual = pVariant1->ulVal == pVariant2->ulVal; - } - break; - case VT_UI8: - case VT_I8: - if (VT_I8 == pVariant2->vt || VT_UI8 == pVariant2->vt) - { - fEqual = pVariant1->ullVal == pVariant2->ullVal; - } - break; - default: - fEqual = VARCMP_EQ == ::VarCmp(pVariant1, - pVariant2, - LOCALE_INVARIANT, - NORM_IGNORECASE); - } - - return fEqual; -} - -BOOL DAPI CompareVariantPath( - __in VARIANT* pVariant1, - __in VARIANT* pVariant2 - ) -{ - HRESULT hr = S_OK; - BOOL fEqual = FALSE; - LPWSTR wzValue1 = NULL; - LPWSTR wzValue2 = NULL; - - if (ISSTRINGVARIANT(pVariant1->vt)) - { - hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - IisExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal); - } - - if (ISSTRINGVARIANT(pVariant2->vt)) - { - hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - IisExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal); - } - - fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1); - -LExit: - ReleaseNullStr(wzValue1); - ReleaseNullStr(wzValue2); - return fEqual; -} - -BOOL DAPI IsMatchingAppHostElementCallback( - __in IAppHostElement *pElement, - __in_bcount(sizeof(IIS7_APPHOSTELEMENTCOMPARISON)) LPVOID pContext - ) -{ - IIS7_APPHOSTELEMENTCOMPARISON* pComparison = (IIS7_APPHOSTELEMENTCOMPARISON*) pContext; - - return Iis7IsMatchingAppHostElement(pElement, pComparison); -} - -extern "C" BOOL DAPI Iis7IsMatchingAppHostElement( - __in IAppHostElement *pElement, - __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison - ) -{ - HRESULT hr = S_OK; - BOOL fResult = FALSE; - IAppHostProperty *pProperty = NULL; - BSTR bstrElementName = NULL; - - VARIANT vPropValue; - ::VariantInit(&vPropValue); - - // Use the default comparator if a comparator is not specified - VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault; - - hr = pElement->get_Name(&bstrElementName); - IisExitOnFailure(hr, "Failed to get name of element"); - if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1)) - { - ExitFunction(); - } - - hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue); - IisExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName); - - if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue)) - { - fResult = TRUE; - } - -LExit: - ReleaseBSTR(bstrElementName); - ReleaseVariant(vPropValue); - ReleaseObject(pProperty); - - return fResult; -} - -BOOL DAPI IsMatchingAppHostMethod( - __in IAppHostMethod *pMethod, - __in LPCWSTR wzMethodName - ) -{ - HRESULT hr = S_OK; - BOOL fResult = FALSE; - BSTR bstrName = NULL; - - hr = pMethod->get_Name(&bstrName); - IisExitOnFailure(hr, "Failed to get name of element"); - - Assert(bstrName); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1)) - { - fResult = TRUE; - } - -LExit: - ReleaseBSTR(bstrName); - - return fResult; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementPath( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; - VARIANT vtValue = { }; - ::VariantInit(&vtValue); - - vtValue.vt = VT_BSTR; - vtValue.bstrVal = ::SysAllocString(wzAttributeValue); - IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - comparison.sczElementName = wzElementName; - comparison.sczAttributeName = wzAttributeName; - comparison.pvAttributeValue = &vtValue; - comparison.pComparator = CompareVariantPath; - - hr = Iis7EnumAppHostElements(pCollection, - IsMatchingAppHostElementCallback, - &comparison, - ppElement, - pdwIndex); - -LExit: - ReleaseVariant(vtValue); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementString( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - VARIANT vtValue; - ::VariantInit(&vtValue); - - vtValue.vt = VT_BSTR; - vtValue.bstrVal = ::SysAllocString(wzAttributeValue); - IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = Iis7FindAppHostElementVariant(pCollection, - wzElementName, - wzAttributeName, - &vtValue, - ppElement, - pdwIndex); - -LExit: - ReleaseVariant(vtValue); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementInteger( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in DWORD dwAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - VARIANT vtValue; - ::VariantInit(&vtValue); - - vtValue.vt = VT_UI4; - vtValue.ulVal = dwAttributeValue; - - hr = Iis7FindAppHostElementVariant(pCollection, - wzElementName, - wzAttributeName, - &vtValue, - ppElement, - pdwIndex); - - ReleaseVariant(vtValue); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementVariant( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in VARIANT* pvAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; - comparison.sczElementName = wzElementName; - comparison.sczAttributeName = wzAttributeName; - comparison.pvAttributeValue = pvAttributeValue; - comparison.pComparator = CompareVariantDefault; - - return Iis7EnumAppHostElements(pCollection, - IsMatchingAppHostElementCallback, - &comparison, - ppElement, - pdwIndex); -} - -extern "C" HRESULT DAPI Iis7EnumAppHostElements( - __in IAppHostElementCollection *pCollection, - __in ENUMAPHOSTELEMENTPROC pCallback, - __in LPVOID pContext, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - IAppHostElement *pElement = NULL; - DWORD dwElements = 0; - - VARIANT vtIndex; - ::VariantInit(&vtIndex); - - if (NULL != ppElement) - { - *ppElement = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = MAXDWORD; - } - - hr = pCollection->get_Count(&dwElements); - IisExitOnFailure(hr, "Failed get application IAppHostElementCollection count"); - - vtIndex.vt = VT_UI4; - for (DWORD i = 0; i < dwElements; ++i) - { - vtIndex.ulVal = i; - hr = pCollection->get_Item(vtIndex , &pElement); - IisExitOnFailure(hr, "Failed get IAppHostElement element"); - - if (pCallback(pElement, pContext)) - { - if (NULL != ppElement) - { - *ppElement = pElement; - pElement = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = i; - } - break; - } - - ReleaseNullObject(pElement); - } - -LExit: - ReleaseObject(pElement); - ReleaseVariant(vtIndex); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostMethod( - __in IAppHostMethodCollection *pCollection, - __in LPCWSTR wzMethodName, - __out IAppHostMethod** ppMethod, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - IAppHostMethod *pMethod = NULL; - DWORD dwMethods = 0; - - VARIANT vtIndex; - ::VariantInit(&vtIndex); - - if (NULL != ppMethod) - { - *ppMethod = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = MAXDWORD; - } - - hr = pCollection->get_Count(&dwMethods); - IisExitOnFailure(hr, "Failed get application IAppHostMethodCollection count"); - - vtIndex.vt = VT_UI4; - for (DWORD i = 0; i < dwMethods; ++i) - { - vtIndex.ulVal = i; - hr = pCollection->get_Item(vtIndex , &pMethod); - IisExitOnFailure(hr, "Failed get IAppHostMethod element"); - - if (IsMatchingAppHostMethod(pMethod, wzMethodName)) - { - if (NULL != ppMethod) - { - *ppMethod = pMethod; - pMethod = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = i; - } - break; - } - - ReleaseNullObject(pMethod); - } - -LExit: - ReleaseObject(pMethod); - ReleaseVariant(vtIndex); - - return hr; -} diff --git a/src/dutil/inc/aclutil.h b/src/dutil/inc/aclutil.h deleted file mode 100644 index ac03f9a8..00000000 --- a/src/dutil/inc/aclutil.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include - -#define ReleaseSid(x) if (x) { AclFreeSid(x); } -#define ReleaseNullSid(x) if (x) { AclFreeSid(x); x = NULL; } - -#ifdef __cplusplus -extern "C" { -#endif - -// structs -struct ACL_ACCESS -{ - BOOL fDenyAccess; - DWORD dwAccessMask; - - // TODO: consider using a union - LPCWSTR pwzAccountName; // NOTE: the last three items in this structure are ignored if this is not NULL - - SID_IDENTIFIER_AUTHORITY sia; // used if pwzAccountName is NULL - BYTE nSubAuthorityCount; - DWORD nSubAuthority[8]; -}; - -struct ACL_ACE -{ - DWORD dwFlags; - DWORD dwMask; - PSID psid; -}; - - -// functions -HRESULT DAPI AclCheckAccess( - __in HANDLE hToken, - __in ACL_ACCESS* paa - ); -HRESULT DAPI AclCheckAdministratorAccess( - __in HANDLE hToken - ); -HRESULT DAPI AclCheckLocalSystemAccess( - __in HANDLE hToken - ); - -HRESULT DAPI AclGetWellKnownSid( - __in WELL_KNOWN_SID_TYPE wkst, - __deref_out PSID* ppsid - ); -HRESULT DAPI AclGetAccountSid( - __in_opt LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out PSID* ppsid - ); -HRESULT DAPI AclGetAccountSidString( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* ppwzSid - ); - -HRESULT DAPI AclCreateDacl( - __in_ecount(cDeny) ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount(cAllow) ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAcl - ); -HRESULT DAPI AclAddToDacl( - __in ACL* pAcl, - __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAclNew - ); -HRESULT DAPI AclMergeDacls( - __in const ACL* pAcl1, - __in const ACL* pAcl2, - __deref_out ACL** ppAclNew - ); -HRESULT DAPI AclCreateDaclOld( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out ACL** ppAcl - ); -HRESULT DAPI AclCreateSecurityDescriptor( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT DAPI AclCreateSecurityDescriptorFromDacl( - __in ACL* pACL, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT __cdecl AclCreateSecurityDescriptorFromString( - __deref_out SECURITY_DESCRIPTOR** ppsd, - __in_z __format_string LPCWSTR wzSddlFormat, - ... - ); -HRESULT DAPI AclDuplicateSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT DAPI AclGetSecurityDescriptor( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT DAPI AclSetSecurityWithRetry( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __in_opt PSID psidOwner, - __in_opt PSID psidGroup, - __in_opt PACL pDacl, - __in_opt PACL pSacl, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ); - -HRESULT DAPI AclFreeSid( - __in PSID psid - ); -HRESULT DAPI AclFreeDacl( - __in ACL* pACL - ); -HRESULT DAPI AclFreeSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd - ); - -HRESULT DAPI AclAddAdminToSecurityDescriptor( - __in SECURITY_DESCRIPTOR* pSecurity, - __deref_out SECURITY_DESCRIPTOR** ppSecurityNew - ); - -// Following code in acl2util.cpp due to dependency on crypt32.dll. -HRESULT DAPI AclCalculateServiceSidString( - __in LPCWSTR wzServiceName, - __in SIZE_T cchServiceName, - __deref_out_z LPWSTR* psczSid - ); -HRESULT DAPI AclGetAccountSidStringEx( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* psczSid - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/apputil.h b/src/dutil/inc/apputil.h deleted file mode 100644 index 1a1e14f7..00000000 --- a/src/dutil/inc/apputil.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// functions - -/******************************************************************** -AppFreeCommandLineArgs - frees argv from AppParseCommandLine. - -********************************************************************/ -void DAPI AppFreeCommandLineArgs( - __in LPWSTR* argv - ); - -void DAPI AppInitialize( - __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], - __in DWORD cSafelyLoadSystemDlls - ); - -/******************************************************************** -AppInitializeUnsafe - initializes without the full standard safety - precautions for an application. - -********************************************************************/ -void DAPI AppInitializeUnsafe(); - -/******************************************************************** -AppParseCommandLine - parses the command line using CommandLineToArgvW. - The caller must free the value of pArgv on success - by calling AppFreeCommandLineArgs. - -********************************************************************/ -DAPI_(HRESULT) AppParseCommandLine( - __in LPCWSTR wzCommandLine, - __in int* argc, - __in LPWSTR** pArgv - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/apuputil.h b/src/dutil/inc/apuputil.h deleted file mode 100644 index f26a12b7..00000000 --- a/src/dutil/inc/apuputil.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } -#define ReleaseNullApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } - - -const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn"; - -typedef enum APUP_HASH_ALGORITHM -{ - APUP_HASH_ALGORITHM_UNKNOWN, - APUP_HASH_ALGORITHM_MD5, - APUP_HASH_ALGORITHM_SHA1, - APUP_HASH_ALGORITHM_SHA256, - APUP_HASH_ALGORITHM_SHA512, -} APUP_HASH_ALGORITHM; - - -struct APPLICATION_UPDATE_ENCLOSURE -{ - LPWSTR wzUrl; - LPWSTR wzLocalName; - DWORD64 dw64Size; - - BYTE* rgbDigest; - DWORD cbDigest; - APUP_HASH_ALGORITHM digestAlgorithm; - - BOOL fInstaller; -}; - - -struct APPLICATION_UPDATE_ENTRY -{ - LPWSTR wzApplicationId; - LPWSTR wzApplicationType; - LPWSTR wzTitle; - LPWSTR wzSummary; - LPWSTR wzContentType; - LPWSTR wzContent; - - LPWSTR wzUpgradeId; - BOOL fUpgradeExclusive; - VERUTIL_VERSION* pVersion; - VERUTIL_VERSION* pUpgradeVersion; - - DWORD64 dw64TotalSize; - - DWORD cEnclosures; - APPLICATION_UPDATE_ENCLOSURE* rgEnclosures; -}; - - -struct APPLICATION_UPDATE_CHAIN -{ - LPWSTR wzDefaultApplicationId; - LPWSTR wzDefaultApplicationType; - - DWORD cEntries; - APPLICATION_UPDATE_ENTRY* rgEntries; -}; - - -HRESULT DAPI ApupAllocChainFromAtom( - __in ATOM_FEED* pFeed, - __out APPLICATION_UPDATE_CHAIN** ppChain - ); - -HRESULT DAPI ApupFilterChain( - __in APPLICATION_UPDATE_CHAIN* pChain, - __in VERUTIL_VERSION* pVersion, - __out APPLICATION_UPDATE_CHAIN** ppFilteredChain - ); - -void DAPI ApupFreeChain( - __in APPLICATION_UPDATE_CHAIN* pChain - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/atomutil.h b/src/dutil/inc/atomutil.h deleted file mode 100644 index 9acfc1d5..00000000 --- a/src/dutil/inc/atomutil.h +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); } -#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; } - - -struct ATOM_UNKNOWN_ATTRIBUTE -{ - LPWSTR wzNamespace; - LPWSTR wzAttribute; - LPWSTR wzValue; - - ATOM_UNKNOWN_ATTRIBUTE* pNext; -}; - -struct ATOM_UNKNOWN_ELEMENT -{ - LPWSTR wzNamespace; - LPWSTR wzElement; - LPWSTR wzValue; - - ATOM_UNKNOWN_ATTRIBUTE* pAttributes; - ATOM_UNKNOWN_ELEMENT* pNext; -}; - -struct ATOM_LINK -{ - LPWSTR wzRel; - LPWSTR wzTitle; - LPWSTR wzType; - LPWSTR wzUrl; - LPWSTR wzValue; - DWORD64 dw64Length; - - ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttributes; - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_CONTENT -{ - LPWSTR wzType; - LPWSTR wzUrl; - LPWSTR wzValue; - - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_AUTHOR -{ - LPWSTR wzName; - LPWSTR wzEmail; - LPWSTR wzUrl; -}; - -struct ATOM_CATEGORY -{ - LPWSTR wzLabel; - LPWSTR wzScheme; - LPWSTR wzTerm; - - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_ENTRY -{ - LPWSTR wzId; - LPWSTR wzSummary; - LPWSTR wzTitle; - FILETIME ftPublished; - FILETIME ftUpdated; - - ATOM_CONTENT* pContent; - - DWORD cAuthors; - ATOM_AUTHOR* rgAuthors; - - DWORD cCategories; - ATOM_CATEGORY* rgCategories; - - DWORD cLinks; - ATOM_LINK* rgLinks; - - IXMLDOMNode* pixn; - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_FEED -{ - LPWSTR wzGenerator; - LPWSTR wzIcon; - LPWSTR wzId; - LPWSTR wzLogo; - LPWSTR wzSubtitle; - LPWSTR wzTitle; - FILETIME ftUpdated; - - DWORD cAuthors; - ATOM_AUTHOR* rgAuthors; - - DWORD cCategories; - ATOM_CATEGORY* rgCategories; - - DWORD cEntries; - ATOM_ENTRY* rgEntries; - - DWORD cLinks; - ATOM_LINK* rgLinks; - - IXMLDOMNode* pixn; - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -HRESULT DAPI AtomInitialize( - ); - -void DAPI AtomUninitialize( - ); - -HRESULT DAPI AtomParseFromString( - __in_z LPCWSTR wzAtomString, - __out ATOM_FEED **ppFeed - ); - -HRESULT DAPI AtomParseFromFile( - __in_z LPCWSTR wzAtomFile, - __out ATOM_FEED **ppFeed - ); - -HRESULT DAPI AtomParseFromDocument( - __in IXMLDOMDocument* pixdDocument, - __out ATOM_FEED **ppFeed - ); - -void DAPI AtomFreeFeed( - __in_xcount(pFeed->cItems) ATOM_FEED* pFeed - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h deleted file mode 100644 index 322209e6..00000000 --- a/src/dutil/inc/buffutil.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -// macro definitions - -#define ReleaseBuffer ReleaseMem -#define ReleaseNullBuffer ReleaseNullMem -#define BuffFree MemFree - - -// function declarations - -HRESULT BuffReadNumber( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD* pdw - ); -HRESULT BuffReadNumber64( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD64* pdw64 - ); -HRESULT BuffReadPointer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD_PTR* pdw -); -HRESULT BuffReadString( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPWSTR* pscz - ); -HRESULT BuffReadStringAnsi( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPSTR* pscz - ); -HRESULT BuffReadStream( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_inout_bcount(*pcbStream) BYTE** ppbStream, - __out SIZE_T* pcbStream - ); - -HRESULT BuffWriteNumber( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD dw - ); -HRESULT BuffWriteNumber64( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD64 dw64 - ); -HRESULT BuffWritePointer( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD_PTR dw -); -HRESULT BuffWriteString( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCWSTR scz - ); -HRESULT BuffWriteStringAnsi( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCSTR scz - ); -HRESULT BuffWriteStream( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_bcount(cbStream) const BYTE* pbStream, - __in SIZE_T cbStream - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/butil.h b/src/dutil/inc/butil.h deleted file mode 100644 index d1ec73bc..00000000 --- a/src/dutil/inc/butil.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -enum BUNDLE_INSTALL_CONTEXT -{ - BUNDLE_INSTALL_CONTEXT_MACHINE, - BUNDLE_INSTALL_CONTEXT_USER, -}; - - -/******************************************************************** -BundleGetBundleInfo - Queries the bundle installation metadata for a given property - -RETURNS: - E_INVALIDARG - An invalid parameter was passed to the function. - HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) - The bundle is not installed - HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) - The property is unrecognized - HRESULT_FROM_WIN32(ERROR_MORE_DATA) - A buffer is too small to hold the requested data. - E_NOTIMPL: - Tried to read a bundle attribute for a type which has not been implemented - - All other returns are unexpected returns from other dutil methods. -********************************************************************/ -HRESULT DAPI BundleGetBundleInfo( - __in_z LPCWSTR szBundleId, // Bundle code - __in_z LPCWSTR szAttribute, // attribute name - __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired - __inout_opt LPDWORD pcchValueBuf // in/out buffer character count - ); - -/******************************************************************** -BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code - -NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long. - The first 38 characters are for the GUID, and the last character is for the terminating null character. -RETURNS: - E_INVALIDARG - An invalid parameter was passed to the function. - - All other returns are unexpected returns from other dutil methods. -********************************************************************/ -HRESULT DAPI BundleEnumRelatedBundle( - __in_z LPCWSTR lpUpgradeCode, - __in BUNDLE_INSTALL_CONTEXT context, - __inout PDWORD pdwStartIndex, - __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/cabcutil.h b/src/dutil/inc/cabcutil.h deleted file mode 100644 index 4f0c7b13..00000000 --- a/src/dutil/inc/cabcutil.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include - -// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method -// First argument is the name of splitting cabinet without extension e.g. "cab1" -// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" -// Third argument is the file token of the first file present in the splitting cabinet -typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR); - -#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866) - -#ifdef __cplusplus -extern "C" { -#endif - -extern const int CABC_HANDLE_BYTES; - -// time vs. space trade-off -typedef enum COMPRESSION_TYPE -{ - COMPRESSION_TYPE_NONE, // fastest - COMPRESSION_TYPE_LOW, - COMPRESSION_TYPE_MEDIUM, - COMPRESSION_TYPE_HIGH, // smallest - COMPRESSION_TYPE_MSZIP -} COMPRESSION_TYPE; - -// functions -HRESULT DAPI CabCBegin( - __in_z LPCWSTR wzCab, - __in_z LPCWSTR wzCabDir, - __in DWORD dwMaxFiles, - __in DWORD dwMaxSize, - __in DWORD dwMaxThresh, - __in COMPRESSION_TYPE ct, - __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext - ); -HRESULT DAPI CabCNextCab( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ); -HRESULT DAPI CabCAddFile( - __in_z LPCWSTR wzFile, - __in_z_opt LPCWSTR wzToken, - __in_opt PMSIFILEHASHINFO pmfHash, - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ); -HRESULT DAPI CabCFinish( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, - __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback - ); -void DAPI CabCCancel( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/cabutil.h b/src/dutil/inc/cabutil.h deleted file mode 100644 index 0bedba80..00000000 --- a/src/dutil/inc/cabutil.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// structs - - -// callback function prototypes -typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile); -typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); -typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); -typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod); -typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile); - -typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile); -typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile); -typedef HRESULT (*CAB_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); - -// function type with calling convention of __stdcall that .NET 1.1 understands only -// .NET 2.0 will not need this -typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin); - - -// functions -HRESULT DAPI CabInitialize( - __in BOOL fDelayLoad - ); -void DAPI CabUninitialize( - ); - -HRESULT DAPI CabExtract( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzExtractFile, - __in_z LPCWSTR wzExtractDir, - __in_opt CAB_CALLBACK_PROGRESS pfnProgress, - __in_opt LPVOID pvContext, - __in DWORD64 dw64EmbeddedOffset - ); - -HRESULT DAPI CabEnumerate( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzEnumerateFile, - __in STDCALL_PFNFDINOTIFY pfnNotify, - __in DWORD64 dw64EmbeddedOffset - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/certutil.h b/src/dutil/inc/certutil.h deleted file mode 100644 index 8565c1cf..00000000 --- a/src/dutil/inc/certutil.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#define ReleaseCertStore(p) if (p) { ::CertCloseStore(p, 0); p = NULL; } -#define ReleaseCertContext(p) if (p) { ::CertFreeCertificateContext(p); p = NULL; } -#define ReleaseCertChain(p) if (p) { ::CertFreeCertificateChain(p); p = NULL; } - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI CertReadProperty( - __in PCCERT_CONTEXT pCertContext, - __in DWORD dwProperty, - __out_bcount(*pcbValue) LPVOID pvValue, - __out_opt DWORD* pcbValue - ); - -HRESULT DAPI CertGetAuthenticodeSigningTimestamp( - __in CMSG_SIGNER_INFO* pSignerInfo, - __out FILETIME* pft - ); - -HRESULT DAPI GetCryptProvFromCert( - __in_opt HWND hwnd, - __in PCCERT_CONTEXT pCert, - __out HCRYPTPROV *phCryptProv, - __out DWORD *pdwKeySpec, - __in BOOL *pfDidCryptAcquire, - __deref_opt_out LPWSTR *ppwszTmpContainer, - __deref_opt_out LPWSTR *ppwszProviderName, - __out DWORD *pdwProviderType - ); - -HRESULT DAPI FreeCryptProvFromCert( - __in BOOL fAcquired, - __in HCRYPTPROV hProv, - __in_opt LPWSTR pwszCapiProvider, - __in DWORD dwProviderType, - __in_opt LPWSTR pwszTmpContainer - ); - -HRESULT DAPI GetProvSecurityDesc( - __in HCRYPTPROV hProv, - __deref_out SECURITY_DESCRIPTOR** pSecurity - ); - -HRESULT DAPI SetProvSecurityDesc( - __in HCRYPTPROV hProv, - __in SECURITY_DESCRIPTOR* pSecurity - ); - -BOOL DAPI CertHasPrivateKey( - __in PCCERT_CONTEXT pCertContext, - __out_opt DWORD* pdwKeySpec - ); - -HRESULT DAPI CertInstallSingleCertificate( - __in HCERTSTORE hStore, - __in PCCERT_CONTEXT pCertContext, - __in LPCWSTR wzName - ); -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/conutil.h b/src/dutil/inc/conutil.h deleted file mode 100644 index 38aaea84..00000000 --- a/src/dutil/inc/conutil.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ConsoleExitOnFailureSource(d, x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } -#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; } } -#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; } -#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; } -#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; } - - -#define ConsoleExitOnFailure(x, c, f, ...) ConsoleExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) -#define ConsoleExitOnLastError(x, c, f, ...) ConsoleExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) -#define ConsoleExitOnNull(p, x, e, c, f, ...) ConsoleExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, c, f, __VA_ARGS__) -#define ConsoleExitOnNullWithLastError(p, x, c, f, ...) ConsoleExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, c, f, __VA_ARGS__) -#define ConsoleExitWithLastError(x, c, f, ...) ConsoleExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) - -// enums -typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR; - -// structs - -// functions -HRESULT DAPI ConsoleInitialize(); -void DAPI ConsoleUninitialize(); - -void DAPI ConsoleGreen(); -void DAPI ConsoleRed(); -void DAPI ConsoleYellow(); -void DAPI ConsoleNormal(); - -HRESULT DAPI ConsoleWrite( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ); -HRESULT DAPI ConsoleWriteLine( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ); -HRESULT DAPI ConsoleWriteError( - HRESULT hrError, - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI ConsoleReadW( - __deref_out_z LPWSTR* ppwzBuffer - ); - -HRESULT DAPI ConsoleReadStringA( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, - CONST DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ); -HRESULT DAPI ConsoleReadStringW( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, - CONST DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ); - -HRESULT DAPI ConsoleReadNonBlockingW( - __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, - __out DWORD* pcchSize, - BOOL fReadLine - ); - -HRESULT DAPI ConsoleSetReadHidden(void); -HRESULT DAPI ConsoleSetReadNormal(void); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/cryputil.h b/src/dutil/inc/cryputil.h deleted file mode 100644 index 02492d8a..00000000 --- a/src/dutil/inc/cryputil.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#define ReleaseCryptMsg(p) if (p) { ::CryptMsgClose(p); p = NULL; } - -#ifdef __cplusplus -extern "C" { -#endif - - -// Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE. -#define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE -#define MD5_HASH_LEN 16 -#define SHA1_HASH_LEN 20 -#define SHA256_HASH_LEN 32 -#define SHA512_HASH_LEN 64 - -typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)( - __inout PVOID Memory, - __in ULONG MemoryLength, - __in ULONG OptionFlags - ); - -typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)( - __inout PVOID Memory, - __in ULONG MemoryLength, - __in ULONG OptionFlags - ); - -typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -// function declarations - -HRESULT DAPI CrypInitialize(); -void DAPI CrypUninitialize(); - -HRESULT DAPI CrypDecodeObject( - __in_z LPCSTR szStructType, - __in_ecount(cbData) const BYTE* pbData, - __in DWORD cbData, - __in DWORD dwFlags, - __out LPVOID* ppvObject, - __out_opt DWORD* pcbObject - ); - -HRESULT DAPI CrypMsgGetParam( - __in HCRYPTMSG hCryptMsg, - __in DWORD dwType, - __in DWORD dwIndex, - __out LPVOID* ppvData, - __out_opt DWORD* pcbData - ); - -HRESULT DAPI CrypHashFile( - __in_z LPCWSTR wzFilePath, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ); - -HRESULT DAPI CrypHashFileHandle( - __in HANDLE hFile, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ); - -HRESULT DAPI CrypHashBuffer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash - ); - -HRESULT DAPI CrypEncryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -HRESULT DAPI CrypDecryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/deputil.h b/src/dutil/inc/deputil.h deleted file mode 100644 index bfe235f3..00000000 --- a/src/dutil/inc/deputil.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); } -#define ReleaseNullDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); rg = NULL; } - -typedef struct _DEPENDENCY -{ - LPWSTR sczKey; - LPWSTR sczName; -} DEPENDENCY; - - -/*************************************************************************** - DepGetProviderInformation - gets the various pieces of data registered - with a dependency. - - Note: Returns E_NOTFOUND if the dependency was not found. -***************************************************************************/ -DAPI_(HRESULT) DepGetProviderInformation( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __deref_out_z_opt LPWSTR* psczId, - __deref_out_z_opt LPWSTR* psczName, - __deref_out_z_opt LPWSTR* psczVersion - ); - -/*************************************************************************** - DepCheckDependency - Checks that the dependency is registered and within - the proper version range. - - Note: Returns E_NOTFOUND if the dependency was not found. -***************************************************************************/ -DAPI_(HRESULT) DepCheckDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes, - __in STRINGDICT_HANDLE sdDependencies, - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies - ); - -/*************************************************************************** - DepCheckDependents - Checks if any dependents are still installed for the - given provider key. - -***************************************************************************/ -DAPI_(HRESULT) DepCheckDependents( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __reserved int iAttributes, - __in C_STRINGDICT_HANDLE sdIgnoredDependents, - __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, - __inout LPUINT pcDependents - ); - -/*************************************************************************** - DepRegisterDependency - Registers the dependency provider. - -***************************************************************************/ -DAPI_(HRESULT) DepRegisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z LPCWSTR wzVersion, - __in_z LPCWSTR wzDisplayName, - __in_z_opt LPCWSTR wzId, - __in int iAttributes - ); - -/*************************************************************************** - DepDependentExists - Determines if a dependent is registered. - - Note: Returns S_OK if dependent is registered. - Returns E_FILENOTFOUND if dependent is not registered -***************************************************************************/ -DAPI_(HRESULT) DepDependentExists( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ); - -/*************************************************************************** - DepRegisterDependent - Registers a dependent under the dependency provider. - -***************************************************************************/ -DAPI_(HRESULT) DepRegisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes - ); - -/*************************************************************************** - DepUnregisterDependency - Removes the dependency provider. - - Note: Caller should call CheckDependents prior to remove a dependency. - Returns E_FILENOTFOUND if the dependency is not registered. -***************************************************************************/ -DAPI_(HRESULT) DepUnregisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey - ); - -/*************************************************************************** - DepUnregisterDependent - Removes a dependent under the dependency provider. - - Note: Returns E_FILENOTFOUND if neither the dependency or dependent are - registered. - ***************************************************************************/ -DAPI_(HRESULT) DepUnregisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ); - -/*************************************************************************** - DependencyArrayAlloc - Allocates or expands an array of DEPENDENCY structs. - -***************************************************************************/ -DAPI_(HRESULT) DepDependencyArrayAlloc( - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies, - __in_z LPCWSTR wzKey, - __in_z_opt LPCWSTR wzName - ); - -/*************************************************************************** - DepDependencyArrayFree - Frees an array of DEPENDENCY structs. - -***************************************************************************/ -DAPI_(void) DepDependencyArrayFree( - __in_ecount(cDependencies) DEPENDENCY* rgDependencies, - __in UINT cDependencies - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dictutil.h b/src/dutil/inc/dictutil.h deleted file mode 100644 index f0a3bb5a..00000000 --- a/src/dutil/inc/dictutil.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); } -#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; } - -typedef void* STRINGDICT_HANDLE; -typedef const void* C_STRINGDICT_HANDLE; - -extern const int STRINGDICT_HANDLE_BYTES; - -typedef enum DICT_FLAG -{ - DICT_FLAG_NONE = 0, - DICT_FLAG_CASEINSENSITIVE = 1 -} DICT_FLAG; - -HRESULT DAPI DictCreateWithEmbeddedKey( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in_opt void **ppvArray, - __in size_t cByteOffset, - __in DICT_FLAG dfFlags - ); -HRESULT DAPI DictCreateStringList( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in DICT_FLAG dfFlags - ); -HRESULT DAPI DictCreateStringListFromArray( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray, - __in DICT_FLAG dfFlags - ); -HRESULT DAPI DictCompareStringListToArray( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray - ); -HRESULT DAPI DictAddKey( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR szString - ); -HRESULT DAPI DictAddValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in void *pvValue - ); -HRESULT DAPI DictKeyExists( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR szString - ); -HRESULT DAPI DictGetValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR szString, - __out void **ppvValue - ); -void DAPI DictDestroy( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dirutil.h b/src/dutil/inc/dirutil.h deleted file mode 100644 index 539b3a73..00000000 --- a/src/dutil/inc/dirutil.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -typedef enum DIR_DELETE -{ - DIR_DELETE_FILES = 1, - DIR_DELETE_RECURSE = 2, - DIR_DELETE_SCHEDULE = 4, -} DIR_DELETE; - -#ifdef __cplusplus -extern "C" { -#endif - -BOOL DAPI DirExists( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ); - -HRESULT DAPI DirCreateTempPath( - __in_z LPCWSTR wzPrefix, - __out_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD cchPath - ); - -HRESULT DAPI DirEnsureExists( - __in_z LPCWSTR wzPath, - __in_opt LPSECURITY_ATTRIBUTES psa - ); - -HRESULT DAPI DirEnsureDelete( - __in_z LPCWSTR wzPath, - __in BOOL fDeleteFiles, - __in BOOL fRecurse - ); - -HRESULT DAPI DirEnsureDeleteEx( - __in_z LPCWSTR wzPath, - __in DWORD dwFlags - ); - -DWORD DAPI DirDeleteEmptyDirectoriesToRoot( - __in_z LPCWSTR wzPath, - __in DWORD dwFlags - ); - -HRESULT DAPI DirGetCurrent( - __deref_out_z LPWSTR* psczCurrentDirectory - ); - -HRESULT DAPI DirSetCurrent( - __in_z LPCWSTR wzDirectory - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/dlutil.h b/src/dutil/inc/dlutil.h deleted file mode 100644 index 3e95103a..00000000 --- a/src/dutil/inc/dlutil.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef HRESULT (WINAPI *LPAUTHENTICATION_ROUTINE)( - __in LPVOID pVoid, - __in HINTERNET hUrl, - __in long lHttpCode, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ); - -typedef int (WINAPI *LPCANCEL_ROUTINE)( - __in HRESULT hrError, - __in_z_opt LPCWSTR wzError, - __in BOOL fAllowRetry, - __in_opt LPVOID pvContext - ); - -// structs -typedef struct _DOWNLOAD_SOURCE -{ - LPWSTR sczUrl; - LPWSTR sczUser; - LPWSTR sczPassword; -} DOWNLOAD_SOURCE; - -typedef struct _DOWNLOAD_CACHE_CALLBACK -{ - LPPROGRESS_ROUTINE pfnProgress; - LPCANCEL_ROUTINE pfnCancel; - LPVOID pv; -} DOWNLOAD_CACHE_CALLBACK; - -typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK -{ - LPAUTHENTICATION_ROUTINE pfnAuthenticate; - LPVOID pv; -} DOWNLOAD_AUTHENTICATION_CALLBACK; - - -// functions - -HRESULT DAPI DownloadUrl( - __in DOWNLOAD_SOURCE* pDownloadSource, - __in DWORD64 dw64AuthoredDownloadSize, - __in LPCWSTR wzDestinationPath, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ); - - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h deleted file mode 100644 index b30e2332..00000000 --- a/src/dutil/inc/dpiutil.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// from WinUser.h -#ifndef WM_DPICHANGED -#define WM_DPICHANGED 0x02E0 -#endif -#ifndef USER_DEFAULT_SCREEN_DPI -#define USER_DEFAULT_SCREEN_DPI 96 -#endif - -typedef enum DPIU_AWARENESS -{ - DPIU_AWARENESS_NONE = 0x0, - DPIU_AWARENESS_SYSTEM = 0x1, - DPIU_AWARENESS_PERMONITOR = 0x2, - DPIU_AWARENESS_PERMONITORV2 = 0x4, - DPIU_AWARENESS_GDISCALED = 0x8, -} DPIU_PROCESS_AWARENESS; - -typedef struct _DPIU_MONITOR_CONTEXT -{ - UINT nDpi; - MONITORINFOEXW mi; -} DPIU_MONITOR_CONTEXT; - -typedef struct _DPIU_WINDOW_CONTEXT -{ - UINT nDpi; -} DPIU_WINDOW_CONTEXT; - -typedef BOOL (APIENTRY* PFN_ADJUSTWINDOWRECTEXFORDPI)( - __in LPRECT lpRect, - __in DWORD dwStyle, - __in BOOL bMenu, - __in DWORD dwExStyle, - __in UINT dpi - ); -typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( - __in HWND hwnd - ); -typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARE)(); -typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARENESSCONTEXT)( - __in DPI_AWARENESS_CONTEXT value - ); - -#ifdef DPI_ENUMS_DECLARED -typedef HRESULT(APIENTRY* PFN_GETDPIFORMONITOR)( - __in HMONITOR hmonitor, - __in MONITOR_DPI_TYPE dpiType, - __in UINT* dpiX, - __in UINT* dpiY - ); -typedef HRESULT(APIENTRY* PFN_SETPROCESSDPIAWARENESS)( - __in PROCESS_DPI_AWARENESS value - ); -#endif - -void DAPI DpiuInitialize(); -void DAPI DpiuUninitialize(); - -/******************************************************************** - DpiuAdjustWindowRect - calculate the required size of the window rectangle, - based on the desired size of the client rectangle - and the provided DPI. - -*******************************************************************/ -void DAPI DpiuAdjustWindowRect( - __in RECT* pWindowRect, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle, - __in UINT nDpi - ); - -/******************************************************************** - DpiuGetMonitorContextFromPoint - get the DPI context of the monitor from the given point. - -*******************************************************************/ -HRESULT DAPI DpiuGetMonitorContextFromPoint( - __in const POINT* pt, - __out DPIU_MONITOR_CONTEXT** ppMonitorContext - ); - -/******************************************************************** - DpiuGetWindowContext - get the DPI context of the given window. - -*******************************************************************/ -void DAPI DpiuGetWindowContext( - __in HWND hWnd, - __in DPIU_WINDOW_CONTEXT* pWindowContext - ); - -/******************************************************************** - DpiuScaleValue - scale the value to the target DPI. - -*******************************************************************/ -int DAPI DpiuScaleValue( - __in int nDefaultDpiValue, - __in UINT nTargetDpi - ); - -/******************************************************************** - DpiuSetProcessDpiAwareness - set the process DPI awareness. The ranking is - PERMONITORV2 > PERMONITOR > SYSTEM > GDISCALED > NONE. - -*******************************************************************/ -HRESULT DAPI DpiuSetProcessDpiAwareness( - __in DPIU_AWARENESS supportedAwareness, - __in_opt DPIU_AWARENESS* pSelectedAwareness - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h deleted file mode 100644 index fc9ec0f4..00000000 --- a/src/dutil/inc/dutil.h +++ /dev/null @@ -1,190 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "dutilsources.h" - -#define DAPI __stdcall -#define DAPIV __cdecl // used only for functions taking variable length arguments - -#define DAPI_(type) EXTERN_C type DAPI -#define DAPIV_(type) EXTERN_C type DAPIV - - -// asserts and traces -typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); - -typedef void (CALLBACK *DUTIL_CALLBACK_TRACEERROR)( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -#ifdef __cplusplus -extern "C" { -#endif - -/******************************************************************** - DutilInitialize - initialize dutil. - -*******************************************************************/ -HRESULT DAPI DutilInitialize( - __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback - ); - -/******************************************************************** - DutilUninitialize - uninitialize dutil. - -*******************************************************************/ -void DAPI DutilUninitialize(); - -void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule); -void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn); -void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine); -void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z __format_string LPCSTR szMessage); - -void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames); -REPORT_LEVEL DAPI Dutil_TraceGetLevel(); -void DAPIV Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...); -void DAPIV Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); -void 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, ...); -void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError); - -#ifdef __cplusplus -} -#endif - - -#ifdef DEBUG - -#define AssertSetModule(m) (void)Dutil_SetAssertModule(m) -#define AssertSetDisplayFunction(pfn) (void)Dutil_SetAssertDisplayFunction(pfn) -#define Assert(f) ((f) ? (void)0 : (void)Dutil_Assert(__FILE__, __LINE__)) -#define AssertSz(f, sz) ((f) ? (void)0 : (void)Dutil_AssertSz(__FILE__, __LINE__, sz)) - -#define TraceSetLevel(l, f) (void)Dutil_TraceSetLevel(l, f) -#define TraceGetLevel() (REPORT_LEVEL)Dutil_TraceGetLevel() -#define Trace(l, f, ...) (void)Dutil_Trace(__FILE__, __LINE__, l, f, __VA_ARGS__) -#define TraceError(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_ERROR, x, f, __VA_ARGS__) -#define TraceErrorDebug(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_DEBUG, x, f, __VA_ARGS__) - -#else // !DEBUG - -#define AssertSetModule(m) -#define AssertSetDisplayFunction(pfn) -#define Assert(f) -#define AssertSz(f, sz) - -#define TraceSetLevel(l, f) -#define Trace(l, f, ...) -#define TraceError(x, f, ...) -#define TraceErrorDebug(x, f, ...) - -#endif // DEBUG - -// DUTIL_SOURCE_DEFAULT can be overriden -#ifndef DUTIL_SOURCE_DEFAULT -#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_UNKNOWN -#endif - -// Exit macros -#define ExitFunction() { goto LExit; } -#define ExitFunction1(x) { x; goto LExit; } - -#define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; } - -#define ExitTraceSource(d, x, s, ...) { TraceError(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_ERROR, d, x, s, __VA_ARGS__); } -#define ExitTraceDebugSource(d, x, s, ...) { TraceErrorDebug(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, d, x, s, __VA_ARGS__); } - -#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; } } -#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; } } -#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; } -#define ExitOnFailureSource(d, x, s, ...) if (FAILED(x)) { ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnRootFailureSource(d, x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnFailureDebugTraceSource(d, x, s, ...) if (FAILED(x)) { ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } -#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; } -#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; } -#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; } -#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; } -#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; } - -#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) -#define ExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) -#define ExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) -#define ExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) -#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__) - -// release macros -#define ReleaseObject(x) if (x) { x->Release(); } -#define ReleaseObjectArray(prg, cel) if (prg) { for (DWORD Dutil_ReleaseObjectArrayIndex = 0; Dutil_ReleaseObjectArrayIndex < cel; ++Dutil_ReleaseObjectArrayIndex) { ReleaseObject(prg[Dutil_ReleaseObjectArrayIndex]); } ReleaseMem(prg); } -#define ReleaseVariant(x) { ::VariantClear(&x); } -#define ReleaseNullObject(x) if (x) { (x)->Release(); x = NULL; } -#define ReleaseCertificate(x) if (x) { ::CertFreeCertificateContext(x); x=NULL; } -#define ReleaseHandle(x) if (x) { ::CloseHandle(x); x = NULL; } - - -// useful defines and macros -#define Unused(x) ((void)x) - -#ifndef countof -#if 1 -#define countof(ary) (sizeof(ary) / sizeof(ary[0])) -#else -#ifndef __cplusplus -#define countof(ary) (sizeof(ary) / sizeof(ary[0])) -#else -template static char countofVerify(void const *, T) throw() { return 0; } -template static void countofVerify(T *const, T *const *) throw() {}; -#define countof(arr) (sizeof(countofVerify(arr,&(arr))) * sizeof(arr)/sizeof(*(arr))) -#endif -#endif -#endif - -#define roundup(x, n) roundup_typed(x, n, DWORD) -#define roundup_typed(x, n, t) (((t)(x) + ((t)(n) - 1)) & ~((t)(n) - 1)) - -#define HRESULT_FROM_RPC(x) ((HRESULT) ((x) | FACILITY_RPC)) - -#ifndef MAXSIZE_T -#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0)) -#endif - -typedef const BYTE* LPCBYTE; - -#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) -#define E_PATHNOTFOUND HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) -#define E_INVALIDDATA HRESULT_FROM_WIN32(ERROR_INVALID_DATA) -#define E_INVALIDSTATE HRESULT_FROM_WIN32(ERROR_INVALID_STATE) -#define E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) -#define E_MOREDATA HRESULT_FROM_WIN32(ERROR_MORE_DATA) -#define E_NOMOREITEMS HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) -#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) -#define E_MODNOTFOUND HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) -#define E_BADCONFIGURATION HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION) - -#define AddRefAndRelease(x) { x->AddRef(); x->Release(); } - -#define MAKEDWORD(lo, hi) ((DWORD)MAKELONG(lo, hi)) -#define MAKEQWORDVERSION(mj, mi, b, r) (((DWORD64)MAKELONG(r, b)) | (((DWORD64)MAKELONG(mi, mj)) << 32)) - - -#ifdef __cplusplus -extern "C" { -#endif - -// other functions -HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule); -HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h deleted file mode 100644 index 7d512cb3..00000000 --- a/src/dutil/inc/dutilsources.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -typedef enum DUTIL_SOURCE -{ - DUTIL_SOURCE_UNKNOWN, - DUTIL_SOURCE_ACLUTIL, - DUTIL_SOURCE_APPUTIL, - DUTIL_SOURCE_APUPUTIL, - DUTIL_SOURCE_ATOMUTIL, - DUTIL_SOURCE_BUFFUTIL, - DUTIL_SOURCE_BUTIL, - DUTIL_SOURCE_CABCUTIL, - DUTIL_SOURCE_CABUTIL, - DUTIL_SOURCE_CERTUTIL, - DUTIL_SOURCE_CONUTIL, - DUTIL_SOURCE_CRYPUTIL, - DUTIL_SOURCE_DEPUTIL, - DUTIL_SOURCE_DICTUTIL, - DUTIL_SOURCE_DIRUTIL, - DUTIL_SOURCE_DLUTIL, - DUTIL_SOURCE_DPIUTIL, - DUTIL_SOURCE_DUTIL, - DUTIL_SOURCE_ESEUTIL, - DUTIL_SOURCE_FILEUTIL, - DUTIL_SOURCE_GDIPUTIL, - DUTIL_SOURCE_GUIDUTIL, - DUTIL_SOURCE_IIS7UTIL, - DUTIL_SOURCE_INETUTIL, - DUTIL_SOURCE_INIUTIL, - DUTIL_SOURCE_JSONUTIL, - DUTIL_SOURCE_LOCUTIL, - DUTIL_SOURCE_LOGUTIL, - DUTIL_SOURCE_MEMUTIL, - DUTIL_SOURCE_METAUTIL, - DUTIL_SOURCE_MONUTIL, - DUTIL_SOURCE_OSUTIL, - DUTIL_SOURCE_PATHUTIL, - DUTIL_SOURCE_PERFUTIL, - DUTIL_SOURCE_POLCUTIL, - DUTIL_SOURCE_PROCUTIL, - DUTIL_SOURCE_REGUTIL, - DUTIL_SOURCE_RESRUTIL, - DUTIL_SOURCE_RESWUTIL, - DUTIL_SOURCE_REXUTIL, - DUTIL_SOURCE_RMUTIL, - DUTIL_SOURCE_RSSUTIL, - DUTIL_SOURCE_SCEUTIL, - DUTIL_SOURCE_SCZUTIL, - DUTIL_SOURCE_SHELUTIL, - DUTIL_SOURCE_SQLUTIL, - DUTIL_SOURCE_SRPUTIL, - DUTIL_SOURCE_STRUTIL, - DUTIL_SOURCE_SVCUTIL, - DUTIL_SOURCE_THMUTIL, - DUTIL_SOURCE_TIMEUTIL, - DUTIL_SOURCE_UNCUTIL, - DUTIL_SOURCE_URIUTIL, - DUTIL_SOURCE_USERUTIL, - DUTIL_SOURCE_WIUTIL, - DUTIL_SOURCE_WUAUTIL, - DUTIL_SOURCE_XMLUTIL, - DUTIL_SOURCE_VERUTIL, - - DUTIL_SOURCE_EXTERNAL = 256, -} DUTIL_SOURCE; - -typedef enum REPORT_LEVEL -{ - REPORT_NONE, // turns off report (only valid for XXXSetLevel()) - REPORT_WARNING, // written if want only warnings or reporting is on in general - REPORT_STANDARD, // written if reporting is on - REPORT_VERBOSE, // written only if verbose reporting is on - REPORT_DEBUG, // reporting useful when debugging code - REPORT_ERROR, // always gets reported, but can never be specified -} REPORT_LEVEL; diff --git a/src/dutil/inc/eseutil.h b/src/dutil/inc/eseutil.h deleted file mode 100644 index bea47b2b..00000000 --- a/src/dutil/inc/eseutil.h +++ /dev/null @@ -1,223 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); } -#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; } - -struct ESE_COLUMN_SCHEMA -{ - JET_COLUMNID jcColumn; - LPCWSTR pszName; - JET_COLTYP jcColumnType; - BOOL fKey; // If this column is part of the key of the table - BOOL fFixed; - BOOL fNullable; - BOOL fAutoIncrement; -}; - -struct ESE_TABLE_SCHEMA -{ - JET_TABLEID jtTable; - LPCWSTR pszName; - DWORD dwColumns; - ESE_COLUMN_SCHEMA *pcsColumns; -}; - -struct ESE_DATABASE_SCHEMA -{ - DWORD dwTables; - ESE_TABLE_SCHEMA *ptsTables; -}; - -typedef enum ESE_QUERY_TYPE -{ - ESE_QUERY_EXACT, - ESE_QUERY_FROM_TOP, - ESE_QUERY_FROM_BOTTOM -} ESE_QUERY_TYPE; - -typedef void* ESE_QUERY_HANDLE; - -HRESULT DAPI EseBeginSession( - __out JET_INSTANCE *pjiInstance, - __out JET_SESID *pjsSession, - __in_z LPCWSTR pszInstance, - __in_z LPCWSTR pszPath - ); -HRESULT DAPI EseEndSession( - __in JET_INSTANCE jiInstance, - __in JET_SESID jsSession - ); -HRESULT DAPI EseEnsureDatabase( - __in JET_SESID jsSession, - __in_z LPCWSTR pszFile, - __in ESE_DATABASE_SCHEMA *pdsSchema, - __out JET_DBID* pjdbDb, - __in BOOL fExclusive, - __in BOOL fReadonly - ); -HRESULT DAPI EseCloseDatabase( - __in JET_SESID jsSession, - __in JET_DBID jdbDb - ); -HRESULT DAPI EseCreateTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ); -HRESULT DAPI EseOpenTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ); -HRESULT DAPI EseCloseTable( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ); -HRESULT DAPI EseEnsureColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __in JET_COLTYP jcColumnType, - __in ULONG ulColumnSize, - __in BOOL fFixed, - __in BOOL fNullable, - __out_opt JET_COLUMNID *pjcColumn - ); -HRESULT DAPI EseGetColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __out JET_COLUMNID *pjcColumn - ); -HRESULT DAPI EseMoveCursor( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in LONG lRow - ); -HRESULT DAPI EseDeleteRow( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ); -HRESULT DAPI EseBeginTransaction( - __in JET_SESID jsSession - ); -HRESULT DAPI EseRollbackTransaction( - __in JET_SESID jsSession, - __in BOOL fAll - ); -HRESULT DAPI EseCommitTransaction( - __in JET_SESID jsSession - ); -HRESULT DAPI EsePrepareUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ULONG ulPrep - ); -HRESULT DAPI EseFinishUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in BOOL fSeekToInsertedRecord - ); -HRESULT DAPI EseSetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ); -HRESULT DAPI EseSetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in DWORD dwValue - ); -HRESULT DAPI EseSetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in BOOL fValue - ); -HRESULT DAPI EseSetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_z LPCWSTR pszValue - ); -HRESULT DAPI EseSetColumnEmpty( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn - ); -HRESULT DAPI EseGetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ); -HRESULT DAPI EseGetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out DWORD *pdwValue - ); -HRESULT DAPI EseGetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out BOOL *pfValue - ); -HRESULT DAPI EseGetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out LPWSTR *ppszValue - ); - -// Call this once for each key column in the table -HRESULT DAPI EseBeginQuery( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ESE_QUERY_TYPE qtQueryType, - __out ESE_QUERY_HANDLE *peqhHandle - ); -HRESULT DAPI EseSetQueryColumnBinary( - __in ESE_QUERY_HANDLE eqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseSetQueryColumnDword( - __in ESE_QUERY_HANDLE eqhHandle, - __in DWORD dwData, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseSetQueryColumnBool( - __in ESE_QUERY_HANDLE eqhHandle, - __in BOOL fValue, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseSetQueryColumnString( - __in ESE_QUERY_HANDLE eqhHandle, - __in_z LPCWSTR pszString, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseFinishQuery( - __in ESE_QUERY_HANDLE eqhHandle - ); -// Once all columns have been set up, call this and read the result -HRESULT DAPI EseRunQuery( - __in ESE_QUERY_HANDLE eqhHandle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h deleted file mode 100644 index d3e326f7..00000000 --- a/src/dutil/inc/fileutil.h +++ /dev/null @@ -1,247 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseFile(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } -#define ReleaseFileHandle(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } -#define ReleaseFileFindHandle(h) if (INVALID_HANDLE_VALUE != h) { ::FindClose(h); h = INVALID_HANDLE_VALUE; } - -#define FILEMAKEVERSION(major, minor, build, revision) static_cast((static_cast(major & 0xFFFF) << 48) \ - | (static_cast(minor & 0xFFFF) << 32) \ - | (static_cast(build & 0xFFFF) << 16) \ - | (static_cast(revision & 0xFFFF))) - -typedef enum FILE_ARCHITECTURE -{ - FILE_ARCHITECTURE_UNKNOWN, - FILE_ARCHITECTURE_X86, - FILE_ARCHITECTURE_X64, - FILE_ARCHITECTURE_IA64, -} FILE_ARCHITECTURE; - -typedef enum FILE_ENCODING -{ - FILE_ENCODING_UNSPECIFIED = 0, - // TODO: distinguish between non-BOM utf-8 and ANSI in the future? - FILE_ENCODING_UTF8, - FILE_ENCODING_UTF8_WITH_BOM, - FILE_ENCODING_UTF16, - FILE_ENCODING_UTF16_WITH_BOM, -} FILE_ENCODING; - - -LPWSTR DAPI FileFromPath( - __in_z LPCWSTR wzPath - ); -HRESULT DAPI FileResolvePath( - __in_z LPCWSTR wzRelativePath, - __out LPWSTR *ppwzFullPath - ); -HRESULT DAPI FileStripExtension( - __in_z LPCWSTR wzFileName, - __out LPWSTR *ppwzFileNameNoExtension - ); -HRESULT DAPI FileChangeExtension( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzNewExtension, - __out LPWSTR *ppwzFileNameNewExtension - ); -HRESULT DAPI FileAddSuffixToBaseName( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzSuffix, - __out_z LPWSTR* psczNewFileName - ); -HRESULT DAPI FileVersionFromString( - __in_z LPCWSTR wzVersion, - __out DWORD *pdwVerMajor, - __out DWORD* pdwVerMinor - ); -HRESULT DAPI FileVersionFromStringEx( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __out DWORD64* pqwVersion - ); -HRESULT DAPI FileVersionToStringEx( - __in DWORD64 qwVersion, - __out LPWSTR* psczVersion - ); -HRESULT DAPI FileSetPointer( - __in HANDLE hFile, - __in DWORD64 dw64Move, - __out_opt DWORD64* pdw64NewPosition, - __in DWORD dwMoveMethod - ); -HRESULT DAPI FileSize( - __in_z LPCWSTR pwzFileName, - __out LONGLONG* pllSize - ); -HRESULT DAPI FileSizeByHandle( - __in HANDLE hFile, - __out LONGLONG* pllSize - ); -BOOL DAPI FileExistsEx( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ); -BOOL DAPI FileExistsAfterRestart( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ); -HRESULT DAPI FileRemoveFromPendingRename( - __in_z LPCWSTR wzPath - ); -HRESULT DAPI FileRead( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath - ); -HRESULT DAPI FileReadEx( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD dwShareMode - ); -HRESULT DAPI FileReadUntil( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD cbMaxRead - ); -HRESULT DAPI FileReadPartial( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK - ); -HRESULT DAPI FileReadPartialEx( - __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK, - __in DWORD dwShareMode - ); -HRESULT DAPI FileReadHandle( - __in HANDLE hFile, - __in_bcount(cbDest) LPBYTE pbDest, - __in SIZE_T cbDest - ); -HRESULT DAPI FileWrite( - __in_z LPCWSTR pwzFileName, - __in DWORD dwFlagsAndAttributes, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData, - __out_opt HANDLE* pHandle - ); -HRESULT DAPI FileWriteHandle( - __in HANDLE hFile, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData - ); -HRESULT DAPI FileCopyUsingHandles( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __out_opt DWORD64* pcbCopied - ); -HRESULT DAPI FileCopyUsingHandlesWithProgress( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - __in_opt LPVOID lpData - ); -HRESULT DAPI FileEnsureCopy( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite - ); -HRESULT DAPI FileEnsureCopyWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ); -HRESULT DAPI FileEnsureMove( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy - ); -HRESULT DAPI FileEnsureMoveWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ); -HRESULT DAPI FileCreateTemp( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ); -HRESULT DAPI FileCreateTempW( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ); -HRESULT DAPI FileVersion( - __in_z LPCWSTR wzFilename, - __out DWORD *pdwVerMajor, - __out DWORD* pdwVerMinor - ); -HRESULT DAPI FileIsSame( - __in_z LPCWSTR wzFile1, - __in_z LPCWSTR wzFile2, - __out LPBOOL lpfSameFile - ); -HRESULT DAPI FileEnsureDelete( - __in_z LPCWSTR wzFile - ); -HRESULT DAPI FileGetTime( - __in_z LPCWSTR wzFile, - __out_opt LPFILETIME lpCreationTime, - __out_opt LPFILETIME lpLastAccessTime, - __out_opt LPFILETIME lpLastWriteTime - ); -HRESULT DAPI FileSetTime( - __in_z LPCWSTR wzFile, - __in_opt const FILETIME *lpCreationTime, - __in_opt const FILETIME *lpLastAccessTime, - __in_opt const FILETIME *lpLastWriteTime - ); -HRESULT DAPI FileResetTime( - __in_z LPCWSTR wzFile - ); -HRESULT DAPI FileExecutableArchitecture( - __in_z LPCWSTR wzFile, - __out FILE_ARCHITECTURE *pArchitecture - ); -HRESULT DAPI FileToString( - __in_z LPCWSTR wzFile, - __out LPWSTR *psczString, - __out_opt FILE_ENCODING *pfeEncoding - ); -HRESULT DAPI FileFromString( - __in_z LPCWSTR wzFile, - __in DWORD dwFlagsAndAttributes, - __in_z LPCWSTR sczString, - __in FILE_ENCODING feEncoding - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/gdiputil.h b/src/dutil/inc/gdiputil.h deleted file mode 100644 index f2145828..00000000 --- a/src/dutil/inc/gdiputil.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#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; } } -#define ExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEFAULT, g, x, s, __VA_ARGS__) - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI GdipInitialize( - __in const Gdiplus::GdiplusStartupInput* pInput, - __out ULONG_PTR* pToken, - __out_opt Gdiplus::GdiplusStartupOutput *pOutput - ); - -void DAPI GdipUninitialize( - __in ULONG_PTR token - ); - -HRESULT DAPI GdipBitmapFromResource( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szId, - __out Gdiplus::Bitmap **ppBitmap - ); - -HRESULT DAPI GdipBitmapFromFile( - __in_z LPCWSTR wzFileName, - __out Gdiplus::Bitmap **ppBitmap - ); - -HRESULT DAPI GdipHresultFromStatus( - __in Gdiplus::Status gs - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/guidutil.h b/src/dutil/inc/guidutil.h deleted file mode 100644 index 478a464f..00000000 --- a/src/dutil/inc/guidutil.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define GUID_STRING_LENGTH 39 - -HRESULT DAPI GuidFixedCreate( - _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid - ); - -HRESULT DAPI GuidCreate( - __deref_out_z LPWSTR* psczGuid - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/iis7util.h b/src/dutil/inc/iis7util.h deleted file mode 100644 index 3572b4e9..00000000 --- a/src/dutil/inc/iis7util.h +++ /dev/null @@ -1,222 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// IIS Config schema names -#define IIS_CONFIG_ADD L"add" -#define IIS_CONFIG_ALLOWED L"allowed" -#define IIS_CONFIG_APPHOST_ROOT L"MACHINE/WEBROOT/APPHOST" -#define IIS_CONFIG_APPLICATION L"application" -#define IIS_CONFIG_APPPOOL L"applicationPool" -#define IIS_CONFIG_APPPOOL_AUTO L"autoStart" -#define IIS_CONFIG_APPPOOL_SECTION L"system.applicationHost/applicationPools" -#define IIS_CONFIG_AUTOSTART L"serverAutoStart" -#define IIS_CONFIG_BINDING L"binding" -#define IIS_CONFIG_BINDINGINFO L"bindingInformation" -#define IIS_CONFIG_BINDINGS L"bindings" -#define IIS_CONFIG_DESC L"description" -#define IIS_CONFIG_EXECUTABLE L"scriptProcessor" -#define IIS_CONFIG_ENABLED L"enabled" -#define IIS_CONFIG_ENABLE32 L"enable32BitAppOnWin64" -#define IIS_CONFIG_FILEEXT L"fileExtension" -#define IIS_CONFIG_FILTER L"filter" -#define IIS_CONFIG_GROUPID L"groupId" -#define IIS_CONFIG_HEADERS L"customHeaders" -#define IIS_CONFIG_HTTPERRORS_SECTION L"system.webServer/httpErrors" -#define IIS_CONFIG_ID L"id" -#define IIS_CONFIG_ISAPI_SECTION L"system.webServer/isapiFilters" -#define IIS_CONFIG_HTTPPROTO_SECTION L"system.webServer/httpProtocol" -#define IIS_CONFIG_LOG_SECTION L"system.applicationHost/log" -#define IIS_CONFIG_LOG_UTF8 L"logInUTF8" -#define IIS_CONFIG_LIMITS L"limits" -#define IIS_CONFIG_PIPELINEMODE L"managedPipelineMode" -#define IIS_CONFIG_MANAGEDRUNTIMEVERSION L"managedRuntimeVersion" -#define IIS_CONFIG_WEBLOG L"logFile" -#define IIS_CONFIG_LOGFORMAT L"logFormat" -#define IIS_CONFIG_MIMEMAP L"mimeMap" -#define IIS_CONFIG_MIMETYPE L"mimeType" -#define IIS_CONFIG_MODULES L"modules" -#define IIS_CONFIG_NAME L"name" -#define IIS_CONFIG_PATH L"path" -#define IIS_CONFIG_PHYSPATH L"physicalPath" -#define IIS_CONFIG_PROTOCOL L"protocol" -#define IIS_CONFIG_RESTRICTION_SECTION L"system.webServer/security/isapiCgiRestriction" -#define IIS_CONFIG_SITE L"site" -#define IIS_CONFIG_SITE_ID L"id" -#define IIS_CONFIG_SITES_SECTION L"system.applicationHost/sites" -#define IIS_CONFIG_CONNECTTIMEOUT L"connectionTimeout" -#define IIS_CONFIG_VDIR L"virtualDirectory" -#define IIS_CONFIG_VALUE L"value" -#define IIS_CONFIG_VERBS L"verb" -#define IIS_CONFIG_WEBLIMITS_SECTION L"system.applicationHost/webLimits" -#define IIS_CONFIG_WEBLIMITS_MAXBAND L"maxGlobalBandwidth" -#define IIS_CONFIG_TRUE L"true" -#define IIS_CONFIG_FALSE L"false" -#define IIS_CONFIG_ERROR L"error" -#define IIS_CONFIG_STATUSCODE L"statusCode" -#define IIS_CONFIG_SUBSTATUS L"subStatusCode" -#define IIS_CONFIG_LANGPATH L"prefixLanguageFilePath" -#define IIS_CONFIG_RESPMODE L"responseMode" -#define IIS_CONFIG_CLEAR L"clear" -#define IIS_CONFIG_RECYCLING L"recycling" -#define IIS_CONFIG_PEROIDRESTART L"periodicRestart" -#define IIS_CONFIG_TIME L"time" -#define IIS_CONFIG_REQUESTS L"requests" -#define IIS_CONFIG_SCHEDULE L"schedule" -#define IIS_CONFIG_MEMORY L"memory" -#define IIS_CONFIG_PRIVMEMORY L"privateMemory" -#define IIS_CONFIG_PROCESSMODEL L"processModel" -#define IIS_CONFIG_IDLETIMEOUT L"idleTimeout" -#define IIS_CONFIG_QUEUELENGTH L"queueLength" -#define IIS_CONFIG_IDENITITYTYPE L"identityType" -#define IIS_CONFIG_LOCALSYSTEM L"LocalSystem" -#define IIS_CONFIG_LOCALSERVICE L"LocalService" -#define IIS_CONFIG_NETWORKSERVICE L"NetworkService" -#define IIS_CONFIG_SPECIFICUSER L"SpecificUser" -#define IIS_CONFIG_APPLICATIONPOOLIDENTITY L"ApplicationPoolIdentity" -#define IIS_CONFIG_USERNAME L"userName" -#define IIS_CONFIG_PASSWORD L"password" -#define IIS_CONFIG_CPU L"cpu" -#define IIS_CONFIG_LIMIT L"limit" -#define IIS_CONFIG_CPU_ACTION L"action" -#define IIS_CONFIG_KILLW3WP L"KillW3wp" -#define IIS_CONFIG_NOACTION L"NoAction" -#define IIS_CONFIG_RESETINTERVAL L"resetInterval" -#define IIS_CONFIG_MAXWRKPROCESSES L"maxProcesses" -#define IIS_CONFIG_HANDLERS_SECTION L"system.webServer/handlers" -#define IIS_CONFIG_DEFAULTDOC_SECTION L"system.webServer/defaultDocument" -#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" -#define IIS_CONFIG_SCRIPTERROR L"scriptErrorSentToBrowser" -#define IIS_CONFIG_STATICCONTENT_SECTION L"system.webServer/staticContent" -#define IIS_CONFIG_HTTPEXPIRES L"httpExpires" -#define IIS_CONFIG_MAXAGE L"cacheControlMaxAge" -#define IIS_CONFIG_CLIENTCACHE L"clientCache" -#define IIS_CONFIG_CACHECONTROLMODE L"cacheControlMode" -#define IIS_CONFIG_USEMAXAGE L"UseMaxAge" -#define IIS_CONFIG_USEEXPIRES L"UseExpires" -#define IIS_CONFIG_CACHECUST L"cacheControlCustom" -#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" -#define IIS_CONFIG_SESSION L"session" -#define IIS_CONFIG_ALLOWSTATE L"allowSessionState" -#define IIS_CONFIG_TIMEOUT L"timeout" -#define IIS_CONFIG_BUFFERING L"bufferingOn" -#define IIS_CONFIG_PARENTPATHS L"enableParentPaths" -#define IIS_CONFIG_SCRIPTLANG L"scriptLanguage" -#define IIS_CONFIG_SCRIPTTIMEOUT L"scriptTimeout" -#define IIS_CONFIG_LIMITS L"limits" -#define IIS_CONFIG_ALLOWDEBUG L"appAllowDebugging" -#define IIS_CONFIG_ALLOWCLIENTDEBUG L"appAllowClientDebug" -#define IIS_CONFIG_CERTIFICATEHASH L"certificateHash" -#define IIS_CONFIG_CERTIFICATESTORENAME L"certificateStoreName" -#define IIS_CONFIG_HTTPLOGGING_SECTION L"system.webServer/httpLogging" -#define IIS_CONFIG_DONTLOG L"dontLog" - -typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID); -typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*); - -HRESULT DAPI Iis7PutPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT vtPut - ); - -HRESULT DAPI Iis7PutPropertyInteger( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in DWORD dValue - ); - -HRESULT DAPI Iis7PutPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPCWSTR wzString - ); - -HRESULT DAPI Iis7PutPropertyBool( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in BOOL fValue); - -HRESULT DAPI Iis7GetPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT* vtGet - ); - -HRESULT DAPI Iis7GetPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPWSTR* psczGet - ); - -struct IIS7_APPHOSTELEMENTCOMPARISON -{ - LPCWSTR sczElementName; - LPCWSTR sczAttributeName; - VARIANT* pvAttributeValue; - VARIANTCOMPARATORPROC pComparator; -}; - -BOOL DAPI Iis7IsMatchingAppHostElement( - __in IAppHostElement *pElement, - __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison - ); - -HRESULT DAPI Iis7FindAppHostElementString( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostElementPath( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostElementInteger( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in DWORD dwAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostElementVariant( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in VARIANT* pvAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7EnumAppHostElements( - __in IAppHostElementCollection *pCollection, - __in ENUMAPHOSTELEMENTPROC pCallback, - __in LPVOID pContext, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostMethod( - __in IAppHostMethodCollection *pCollection, - __in LPCWSTR wzMethodName, - __out IAppHostMethod** ppMethod, - __out DWORD* pdwIndex - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/inetutil.h b/src/dutil/inc/inetutil.h deleted file mode 100644 index 19ace88b..00000000 --- a/src/dutil/inc/inetutil.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } -#define ReleaseNullInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } - - -// functions -HRESULT DAPI InternetGetSizeByHandle( - __in HINTERNET hiFile, - __out LONGLONG* pllSize - ); - -HRESULT DAPI InternetGetCreateTimeByHandle( - __in HINTERNET hiFile, - __out LPFILETIME pft - ); - -HRESULT DAPI InternetQueryInfoString( - __in HINTERNET h, - __in DWORD dwInfo, - __deref_out_z LPWSTR* psczValue - ); - -HRESULT DAPI InternetQueryInfoNumber( - __in HINTERNET h, - __in DWORD dwInfo, - __inout LONG* plInfo - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/iniutil.h b/src/dutil/inc/iniutil.h deleted file mode 100644 index c8503155..00000000 --- a/src/dutil/inc/iniutil.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); } -#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; } - -typedef void* INI_HANDLE; -typedef const void* C_INI_HANDLE; - -extern const int INI_HANDLE_BYTES; - -struct INI_VALUE -{ - LPCWSTR wzName; - LPCWSTR wzValue; - - DWORD dwLineNumber; -}; - -HRESULT DAPI IniInitialize( - __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle - ); -void DAPI IniUninitialize( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle - ); -HRESULT DAPI IniSetOpenTag( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzOpenTagPrefix, - __in_z_opt LPCWSTR wzOpenTagPostfix - ); -HRESULT DAPI IniSetValueStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzValuePrefix, - __in_z_opt LPCWSTR wzValueSeparator - ); -HRESULT DAPI IniSetValueSeparatorException( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z LPCWSTR wzValueNamePrefix - ); -HRESULT DAPI IniSetCommentStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzLinePrefix - ); -HRESULT DAPI IniParse( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzPath, - __out_opt FILE_ENCODING *pfeEncodingFound - ); -// Gets the full value array, this includes values that may have been deleted -// (their value will be NULL) -HRESULT DAPI IniGetValueList( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, - __out DWORD *pcValues - ); -HRESULT DAPI IniGetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __deref_out_z LPWSTR* psczValue - ); -HRESULT DAPI IniSetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __in_z_opt LPCWSTR wzValue - ); -HRESULT DAPI IniWriteFile( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzPath, - __in FILE_ENCODING feOverrideEncoding - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/jsonutil.h b/src/dutil/inc/jsonutil.h deleted file mode 100644 index b05e9970..00000000 --- a/src/dutil/inc/jsonutil.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum JSON_TOKEN -{ - JSON_TOKEN_NONE, - JSON_TOKEN_ARRAY_START, - JSON_TOKEN_ARRAY_VALUE, - JSON_TOKEN_ARRAY_END, - JSON_TOKEN_OBJECT_START, - JSON_TOKEN_OBJECT_KEY, - JSON_TOKEN_OBJECT_VALUE, - JSON_TOKEN_OBJECT_END, - JSON_TOKEN_VALUE, -} JSON_TOKEN; - -typedef struct _JSON_VALUE -{ -} JSON_VALUE; - -typedef struct _JSON_READER -{ - CRITICAL_SECTION cs; - LPWSTR sczJson; - - LPWSTR pwz; - JSON_TOKEN token; -} JSON_READER; - -typedef struct _JSON_WRITER -{ - CRITICAL_SECTION cs; - LPWSTR sczJson; - - JSON_TOKEN* rgTokenStack; - DWORD cTokens; - DWORD cMaxTokens; -} JSON_WRITER; - - -DAPI_(HRESULT) JsonInitializeReader( - __in_z LPCWSTR wzJson, - __in JSON_READER* pReader - ); - -DAPI_(void) JsonUninitializeReader( - __in JSON_READER* pReader - ); - -DAPI_(HRESULT) JsonReadNext( - __in JSON_READER* pReader, - __out JSON_TOKEN* pToken, - __out JSON_VALUE* pValue - ); - -DAPI_(HRESULT) JsonReadValue( - __in JSON_READER* pReader, - __in JSON_VALUE* pValue - ); - -DAPI_(HRESULT) JsonInitializeWriter( - __in JSON_WRITER* pWriter - ); - -DAPI_(void) JsonUninitializeWriter( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteBool( - __in JSON_WRITER* pWriter, - __in BOOL fValue - ); - -DAPI_(HRESULT) JsonWriteNumber( - __in JSON_WRITER* pWriter, - __in DWORD dwValue - ); - -DAPI_(HRESULT) JsonWriteString( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzValue - ); - -DAPI_(HRESULT) JsonWriteArrayStart( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteArrayEnd( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteObjectStart( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteObjectKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ); - -DAPI_(HRESULT) JsonWriteObjectEnd( - __in JSON_WRITER* pWriter - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/locutil.h b/src/dutil/inc/locutil.h deleted file mode 100644 index 38ddda20..00000000 --- a/src/dutil/inc/locutil.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -struct LOC_STRING -{ - LPWSTR wzId; - LPWSTR wzText; - BOOL bOverridable; -}; - -const int LOC_CONTROL_NOT_SET = INT_MAX; - -struct LOC_CONTROL -{ - LPWSTR wzControl; - int nX; - int nY; - int nWidth; - int nHeight; - LPWSTR wzText; -}; - -const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX; - -struct WIX_LOCALIZATION -{ - DWORD dwLangId; - - DWORD cLocStrings; - LOC_STRING* rgLocStrings; - - DWORD cLocControls; - LOC_CONTROL* rgLocControls; -}; - -/******************************************************************** - LocProbeForFile - Searches for a localization file on disk. - -*******************************************************************/ -HRESULT DAPI LocProbeForFile( - __in_z LPCWSTR wzBasePath, - __in_z LPCWSTR wzLocFileName, - __in_z_opt LPCWSTR wzLanguage, - __inout LPWSTR* psczPath - ); - -/******************************************************************** - LocLoadFromFile - Loads a localization file - -*******************************************************************/ -HRESULT DAPI LocLoadFromFile( - __in_z LPCWSTR wzWxlFile, - __out WIX_LOCALIZATION** ppWixLoc - ); - -/******************************************************************** - LocLoadFromResource - loads a localization file from a module's data - resource. - - NOTE: The resource data must be UTF-8 encoded. -*******************************************************************/ -HRESULT DAPI LocLoadFromResource( - __in HMODULE hModule, - __in_z LPCSTR szResource, - __out WIX_LOCALIZATION** ppWixLoc - ); - -/******************************************************************** - LocFree - free memory allocated when loading a localization file - -*******************************************************************/ -void DAPI LocFree( - __in_opt WIX_LOCALIZATION* pWixLoc - ); - -/******************************************************************** - LocLocalizeString - replace any #(loc.id) in a string with the - correct sub string -*******************************************************************/ -HRESULT DAPI LocLocalizeString( - __in const WIX_LOCALIZATION* pWixLoc, - __inout LPWSTR* psczInput - ); - -/******************************************************************** - LocGetControl - returns a control's localization information -*******************************************************************/ -HRESULT DAPI LocGetControl( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_CONTROL** ppLocControl - ); - -/******************************************************************** -LocGetString - returns a string's localization information -*******************************************************************/ -extern "C" HRESULT DAPI LocGetString( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_STRING** ppLocString - ); - -/******************************************************************** -LocAddString - adds a localization string -*******************************************************************/ -extern "C" HRESULT DAPI LocAddString( - __in WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __in_z LPCWSTR wzLocString, - __in BOOL bOverridable - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/logutil.h b/src/dutil/inc/logutil.h deleted file mode 100644 index 426506ee..00000000 --- a/src/dutil/inc/logutil.h +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define LogExitOnFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } -#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; } - -#define LogExitOnFailure(x, i, f, ...) LogExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) -#define LogExitOnRootFailure(x, i, f, ...) LogExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) - -typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)( - __in_z LPCSTR szString, - __in_opt LPVOID pvContext - ); - -// enums - -// structs - -// functions -BOOL DAPI IsLogInitialized(); - -BOOL DAPI IsLogOpen(); - -void DAPI LogInitialize( - __in HMODULE hModule - ); - -HRESULT DAPI LogOpen( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzLog, - __in_z_opt LPCWSTR wzPostfix, - __in_z_opt LPCWSTR wzExt, - __in BOOL fAppend, - __in BOOL fHeader, - __out_z_opt LPWSTR* psczLogPath - ); - -void DAPI LogDisable(); - -void DAPI LogRedirect( - __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, - __in_opt LPVOID pvContext - ); - -HRESULT DAPI LogRename( - __in_z LPCWSTR wzNewPath - ); - -void DAPI LogClose( - __in BOOL fFooter - ); - -void DAPI LogUninitialize( - __in BOOL fFooter - ); - -BOOL DAPI LogIsOpen(); - -HRESULT DAPI LogSetSpecialParams( - __in_z_opt LPCWSTR wzSpecialBeginLine, - __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, - __in_z_opt LPCWSTR wzSpecialEndLine - ); - -REPORT_LEVEL DAPI LogSetLevel( - __in REPORT_LEVEL rl, - __in BOOL fLogChange - ); - -REPORT_LEVEL DAPI LogGetLevel(); - -HRESULT DAPI LogGetPath( - __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, - __in DWORD cchLogPath - ); - -HANDLE DAPI LogGetHandle(); - -HRESULT DAPIV LogString( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI LogStringArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -HRESULT DAPIV LogStringLine( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI LogStringLineArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -HRESULT DAPI LogIdModuleArgs( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in va_list args - ); - -/* - * Wraps LogIdModuleArgs, so inline to save the function call - */ - -inline HRESULT LogId( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - ... - ) -{ - HRESULT hr = S_OK; - va_list args; - - va_start(args, dwLogId); - hr = LogIdModuleArgs(rl, dwLogId, NULL, args); - va_end(args); - - return hr; -} - - -/* - * Wraps LogIdModuleArgs, so inline to save the function call - */ - -inline HRESULT LogIdArgs( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in va_list args - ) -{ - return LogIdModuleArgs(rl, dwLogId, NULL, args); -} - -HRESULT DAPIV LogErrorString( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI LogErrorStringArgs( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -HRESULT DAPI LogErrorIdModule( - __in HRESULT hrError, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzString1, - __in_z_opt LPCWSTR wzString2, - __in_z_opt LPCWSTR wzString3 - ); - -inline HRESULT LogErrorId( - __in HRESULT hrError, - __in DWORD dwLogId, - __in_z_opt LPCWSTR wzString1 = NULL, - __in_z_opt LPCWSTR wzString2 = NULL, - __in_z_opt LPCWSTR wzString3 = NULL - ) -{ - return LogErrorIdModule(hrError, dwLogId, NULL, wzString1, wzString2, wzString3); -} - -HRESULT DAPI LogHeader(); - -HRESULT DAPI LogFooter(); - -HRESULT LogStringWorkRaw( - __in_z LPCSTR szLogData - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/memutil.h b/src/dutil/inc/memutil.h deleted file mode 100644 index 49f86e0a..00000000 --- a/src/dutil/inc/memutil.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseMem(p) if (p) { MemFree(p); } -#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; } - -HRESULT DAPI MemInitialize(); -void DAPI MemUninitialize(); - -LPVOID DAPI MemAlloc( - __in SIZE_T cbSize, - __in BOOL fZero - ); -LPVOID DAPI MemReAlloc( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero - ); -HRESULT DAPI MemReAllocSecure( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero, - __deref_out LPVOID* ppvNew - ); -HRESULT DAPI MemAllocArray( - __inout LPVOID* ppvArray, - __in SIZE_T cbArrayType, - __in DWORD dwItemCount - ); -HRESULT DAPI MemReAllocArray( - __inout LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwNewItemCount - ); -HRESULT DAPI MemEnsureArraySize( - __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ); -HRESULT DAPI MemInsertIntoArray( - __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, - __in DWORD dwInsertIndex, - __in DWORD cInsertItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ); -void DAPI MemRemoveFromArray( - __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, - __in DWORD dwRemoveIndex, - __in DWORD cRemoveItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in BOOL fPreserveOrder - ); -void DAPI MemArraySwapItems( - __inout_bcount(cbArrayType) LPVOID pvArray, - __in DWORD dwIndex1, - __in DWORD dwIndex2, - __in SIZE_T cbArrayType - ); - -HRESULT DAPI MemFree( - __in LPVOID pv - ); -SIZE_T DAPI MemSize( - __in LPCVOID pv - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/metautil.h b/src/dutil/inc/metautil.h deleted file mode 100644 index 31f9ff5c..00000000 --- a/src/dutil/inc/metautil.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// structs - -// prototypes -HRESULT DAPI MetaFindWebBase( - __in IMSAdminBaseW* piMetabase, - __in_z LPCWSTR wzIP, - __in int iPort, - __in_z LPCWSTR wzHeader, - __in BOOL fSecure, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ); -HRESULT DAPI MetaFindFreeWebBase( - __in IMSAdminBaseW* piMetabase, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ); - -HRESULT DAPI MetaOpenKey( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __in DWORD dwAccess, - __in DWORD cRetries, - __out METADATA_HANDLE* pmh - ); -HRESULT DAPI MetaGetValue( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __inout METADATA_RECORD* pmr - ); -void DAPI MetaFreeValue( - __in METADATA_RECORD* pmr - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/monutil.h b/src/dutil/inc/monutil.h deleted file mode 100644 index f644e205..00000000 --- a/src/dutil/inc/monutil.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseMon(mh) if (mh) { MonDestroy(mh); } -#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; } - -typedef void* MON_HANDLE; -typedef const void* C_MON_HANDLE; - -// Defined in regutil.h -enum REG_KEY_BITNESS; - -extern const int MON_HANDLE_BYTES; - -// Note: callbacks must be implemented in a thread-safe manner. They will be called asynchronously by a MonUtil-spawned thread. -// They must also be written to return as soon as possible - they are called from the waiter thread -typedef void (*PFN_MONGENERAL)( - __in HRESULT hr, - __in_opt LPVOID pvContext - ); -// This callback is not specific to any wait - it will notify client of any drive status changes, such as removable drive insertion / removal -typedef void (*PFN_MONDRIVESTATUS)( - __in WCHAR chDrive, - __in BOOL fArriving, - __in_opt LPVOID pvContext - ); -// 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, -// 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, -// so all consumers of MonUtil should be designed with this in mind. -typedef void (*PFN_MONDIRECTORY)( - __in HRESULT hr, - __in_z LPCWSTR wzPath, - __in BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvDirectoryContext - ); -typedef void (*PFN_MONREGKEY)( - __in HRESULT hr, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvRegKeyContext - ); - -// Silence period allows you to avoid lots of notifications when a lot of writes are going on in a directory -// MonUtil will wait until the directory has been "silent" for at least dwSilencePeriodInMs milliseconds -// The drawback to setting this to a value higher than zero is that even single write notifications -// are delayed by this amount -HRESULT DAPI MonCreate( - __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, - __in PFN_MONGENERAL vpfMonGeneral, - __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, - __in_opt PFN_MONDIRECTORY vpfMonDirectory, - __in_opt PFN_MONREGKEY vpfMonRegKey, - __in_opt LPVOID pvContext - ); -// Don't add multiple identical waits! Not only is it wasteful and will cause multiple fires for the exact same change, it will also -// result in slightly odd behavior when you remove a duplicated wait (removing a wait may or may not remove multiple waits) -// 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. -// So instead, de-dupe your wait requests before sending them to MonUtil. -// Special notes for network waits: MonUtil can send false positive notifications (i.e. notifications when nothing had changed) if connection -// to the share is lost and reconnected, because MonUtil can't know for sure whether changes occurred while the connection was lost. -// Also, MonUtil will very every 20 minutes retry even successful network waits, because the underlying Win32 API cannot notify us if a remote server -// had its network cable unplugged or similar sudden failure. When we retry the successful network waits, we will also send a false positive notification, -// 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 -// 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. -HRESULT DAPI MonAddDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzPath, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvDirectoryContext - ); -HRESULT DAPI MonAddRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvRegKeyContext - ); -HRESULT DAPI MonRemoveDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzPath, - __in BOOL fRecursive - ); -HRESULT DAPI MonRemoveRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive - ); -void DAPI MonDestroy( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/osutil.h b/src/dutil/inc/osutil.h deleted file mode 100644 index 2cce6f63..00000000 --- a/src/dutil/inc/osutil.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum OS_VERSION -{ - OS_VERSION_UNKNOWN, - OS_VERSION_WINNT, - OS_VERSION_WIN2000, - OS_VERSION_WINXP, - OS_VERSION_WIN2003, - OS_VERSION_VISTA, - OS_VERSION_WIN2008, - OS_VERSION_WIN7, - OS_VERSION_WIN2008_R2, - OS_VERSION_FUTURE -} OS_VERSION; - -void DAPI OsGetVersion( - __out OS_VERSION* pVersion, - __out DWORD* pdwServicePack - ); -HRESULT DAPI OsCouldRunPrivileged( - __out BOOL* pfPrivileged - ); -HRESULT DAPI OsIsRunningPrivileged( - __out BOOL* pfPrivileged - ); -HRESULT DAPI OsIsUacEnabled( - __out BOOL* pfUacEnabled - ); -HRESULT DAPI OsRtlGetVersion( - __inout RTL_OSVERSIONINFOEXW* pOvix - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h deleted file mode 100644 index 579b8454..00000000 --- a/src/dutil/inc/pathutil.h +++ /dev/null @@ -1,252 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum PATH_EXPAND -{ - PATH_EXPAND_ENVIRONMENT = 0x0001, - PATH_EXPAND_FULLPATH = 0x0002, -} PATH_EXPAND; - - -/******************************************************************* - PathCommandLineAppend - appends a command line argument on to a - string such that ::CommandLineToArgv() will shred them correctly - (i.e. quote arguments with spaces in them). -********************************************************************/ -DAPI_(HRESULT) PathCommandLineAppend( - __deref_inout_z LPWSTR* psczCommandLine, - __in_z LPCWSTR wzArgument - ); - -/******************************************************************* - PathFile - returns a pointer to the file part of the path. -********************************************************************/ -DAPI_(LPWSTR) PathFile( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathExtension - returns a pointer to the extension part of the path - (including the dot). -********************************************************************/ -DAPI_(LPCWSTR) PathExtension( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathGetDirectory - extracts the directory from a path. -********************************************************************/ -DAPI_(HRESULT) PathGetDirectory( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczDirectory - ); - -/******************************************************************* -PathGetParentPath - extracts the parent directory from a full path. -********************************************************************/ -DAPI_(HRESULT) PathGetParentPath( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczDirectory - ); - -/******************************************************************* - PathExpand - gets the full path to a file resolving environment - variables along the way. -********************************************************************/ -DAPI_(HRESULT) PathExpand( - __out LPWSTR *psczFullPath, - __in_z LPCWSTR wzRelativePath, - __in DWORD dwResolveFlags - ); - -/******************************************************************* - PathPrefix - prefixes a full path with \\?\ or \\?\UNC as - appropriate. -********************************************************************/ -DAPI_(HRESULT) PathPrefix( - __inout LPWSTR *psczFullPath - ); - -/******************************************************************* - PathFixedBackslashTerminate - appends a \ if path does not have it - already, but fails if the buffer is - insufficient. -********************************************************************/ -DAPI_(HRESULT) PathFixedBackslashTerminate( - __inout_ecount_z(cchPath) LPWSTR wzPath, - __in SIZE_T cchPath - ); - -/******************************************************************* - PathBackslashTerminate - appends a \ if path does not have it - already. -********************************************************************/ -DAPI_(HRESULT) PathBackslashTerminate( - __inout LPWSTR* psczPath - ); - -/******************************************************************* - PathForCurrentProcess - gets the full path to the currently executing - process or (optionally) a module inside the process. -********************************************************************/ -DAPI_(HRESULT) PathForCurrentProcess( - __inout LPWSTR *psczFullPath, - __in_opt HMODULE hModule - ); - -/******************************************************************* - PathRelativeToModule - gets the name of a file in the same - directory as the current process or (optionally) a module inside - the process -********************************************************************/ -DAPI_(HRESULT) PathRelativeToModule( - __inout LPWSTR *psczFullPath, - __in_opt LPCWSTR wzFileName, - __in_opt HMODULE hModule - ); - -/******************************************************************* - PathCreateTempFile - - Note: if wzDirectory is null, ::GetTempPath() will be used instead. - if wzFileNameTemplate is null, GetTempFileName() will be used instead. -*******************************************************************/ -DAPI_(HRESULT) PathCreateTempFile( - __in_opt LPCWSTR wzDirectory, - __in_opt __format_string LPCWSTR wzFileNameTemplate, - __in DWORD dwUniqueCount, - __in DWORD dwFileAttributes, - __out_opt LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ); - -/******************************************************************* - PathCreateTimeBasedTempFile - creates an empty temp file based on current - system time -********************************************************************/ -DAPI_(HRESULT) PathCreateTimeBasedTempFile( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzPrefix, - __in_z_opt LPCWSTR wzPostfix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ); - -/******************************************************************* - PathCreateTempDirectory - - Note: if wzDirectory is null, ::GetTempPath() will be used instead. -*******************************************************************/ -DAPI_(HRESULT) PathCreateTempDirectory( - __in_opt LPCWSTR wzDirectory, - __in __format_string LPCWSTR wzDirectoryNameTemplate, - __in DWORD dwUniqueCount, - __out LPWSTR* psczTempDirectory - ); - -/******************************************************************* - PathGetKnownFolder - returns the path to a well-known shell folder - -*******************************************************************/ -DAPI_(HRESULT) PathGetKnownFolder( - __in int csidl, - __out LPWSTR* psczKnownFolder - ); - -/******************************************************************* - PathIsAbsolute - returns true if the path is absolute; false - otherwise. -*******************************************************************/ -DAPI_(BOOL) PathIsAbsolute( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathConcat - like .NET's Path.Combine, lets you build up a path - one piece -- file or directory -- at a time. -*******************************************************************/ -DAPI_(HRESULT) PathConcat( - __in_opt LPCWSTR wzPath1, - __in_opt LPCWSTR wzPath2, - __deref_out_z LPWSTR* psczCombined - ); - -/******************************************************************* - PathConcatCch - like .NET's Path.Combine, lets you build up a path - one piece -- file or directory -- at a time. -*******************************************************************/ -DAPI_(HRESULT) PathConcatCch( - __in_opt LPCWSTR wzPath1, - __in SIZE_T cchPath1, - __in_opt LPCWSTR wzPath2, - __in SIZE_T cchPath2, - __deref_out_z LPWSTR* psczCombined - ); - -/******************************************************************* - PathEnsureQuoted - ensures that a path is quoted; optionally, - this function also terminates a directory with a backslash - if it is not already. -*******************************************************************/ -DAPI_(HRESULT) PathEnsureQuoted( - __inout LPWSTR* ppszPath, - __in BOOL fDirectory - ); - -/******************************************************************* - PathCompare - compares the fully expanded path of the two paths using - ::CompareStringW(). -*******************************************************************/ -DAPI_(HRESULT) PathCompare( - __in_z LPCWSTR wzPath1, - __in_z LPCWSTR wzPath2, - __out int* pnResult - ); - -/******************************************************************* - PathCompress - sets the compression state on an existing file or - directory. A no-op on file systems that don't - support compression. -*******************************************************************/ -DAPI_(HRESULT) PathCompress( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathGetHierarchyArray - allocates an array containing, - in order, every parent directory of the specified path, - ending with the actual input path - This function also works with registry subkeys -*******************************************************************/ -DAPI_(HRESULT) PathGetHierarchyArray( - __in_z LPCWSTR wzPath, - __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, - __inout LPUINT pcPathArray - ); - -/******************************************************************* - PathCanonicalizePath - wrapper around PathCanonicalizeW. -*******************************************************************/ -DAPI_(HRESULT) PathCanonicalizePath( - __in_z LPCWSTR wzPath, - __deref_out_z LPWSTR* psczCanonicalized - ); - -/******************************************************************* -PathDirectoryContainsPath - checks if wzPath is located inside - wzDirectory. -*******************************************************************/ -DAPI_(HRESULT) PathDirectoryContainsPath( - __in_z LPCWSTR wzDirectory, - __in_z LPCWSTR wzPath - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/perfutil.h b/src/dutil/inc/perfutil.h deleted file mode 100644 index 7557511d..00000000 --- a/src/dutil/inc/perfutil.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// structs - - -// functions -void DAPI PerfInitialize( - ); -void DAPI PerfClickTime( - __out_opt LARGE_INTEGER* pliElapsed - ); -double DAPI PerfConvertToSeconds( - __in const LARGE_INTEGER* pli - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/polcutil.h b/src/dutil/inc/polcutil.h deleted file mode 100644 index 13618043..00000000 --- a/src/dutil/inc/polcutil.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn"; - -/******************************************************************** -PolcReadNumber - reads a number from policy. - -NOTE: S_FALSE returned if policy not set. -NOTE: out is set to default on S_FALSE or any error. -********************************************************************/ -HRESULT DAPI PolcReadNumber( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in DWORD dwDefault, - __out DWORD* pdw - ); - -/******************************************************************** -PolcReadString - reads a string from policy. - -NOTE: S_FALSE returned if policy not set. -NOTE: out is set to default on S_FALSE or any error. -********************************************************************/ -HRESULT DAPI PolcReadString( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in_z_opt LPCWSTR wzDefault, - __deref_out_z LPWSTR* pscz - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/procutil.h b/src/dutil/inc/procutil.h deleted file mode 100644 index 00f3f358..00000000 --- a/src/dutil/inc/procutil.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// structs -typedef struct _PROC_FILESYSTEMREDIRECTION -{ - BOOL fDisabled; - LPVOID pvRevertState; -} PROC_FILESYSTEMREDIRECTION; - -HRESULT DAPI ProcElevated( - __in HANDLE hProcess, - __out BOOL* pfElevated - ); - -HRESULT DAPI ProcWow64( - __in HANDLE hProcess, - __out BOOL* pfWow64 - ); -HRESULT DAPI ProcDisableWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ); -HRESULT DAPI ProcRevertWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ); - -HRESULT DAPI ProcExec( - __in_z LPCWSTR wzExecutablePath, - __in_z_opt LPCWSTR wzCommandLine, - __in int nCmdShow, - __out HANDLE *phProcess - ); -HRESULT DAPI ProcExecute( - __in_z LPWSTR wzCommand, - __out HANDLE *phProcess, - __out_opt HANDLE *phChildStdIn, - __out_opt HANDLE *phChildStdOutErr - ); -HRESULT DAPI ProcWaitForCompletion( - __in HANDLE hProcess, - __in DWORD dwTimeout, - __out DWORD *pReturnCode - ); -HRESULT DAPI ProcWaitForIds( - __in_ecount(cProcessIds) const DWORD* pdwProcessIds, - __in DWORD cProcessIds, - __in DWORD dwMilliseconds - ); -HRESULT DAPI ProcCloseIds( - __in_ecount(cProcessIds) const DWORD* pdwProcessIds, - __in DWORD cProcessIds - ); - -// following code in proc2utl.cpp due to dependency on PSAPI.DLL. -HRESULT DAPI ProcFindAllIdsFromExeName( - __in_z LPCWSTR wzExeName, - __out DWORD** ppdwProcessIds, - __out DWORD* pcProcessIds - ); - -// following code in proc3utl.cpp due to dependency on Wtsapi32.DLL. -HRESULT DAPI ProcExecuteAsInteractiveUser( - __in_z LPCWSTR wzExecutablePath, - __in_z LPCWSTR wzCommand, - __out HANDLE *phProcess - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/regutil.h b/src/dutil/inc/regutil.h deleted file mode 100644 index 75284940..00000000 --- a/src/dutil/inc/regutil.h +++ /dev/null @@ -1,246 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; } - -typedef enum REG_KEY_BITNESS -{ - REG_KEY_DEFAULT = 0, - REG_KEY_32BIT = 1, - REG_KEY_64BIT = 2 -} REG_KEY_BITNESS; - -typedef LSTATUS (APIENTRY *PFN_REGCREATEKEYEXW)( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __reserved DWORD Reserved, - __in_opt LPWSTR lpClass, - __in DWORD dwOptions, - __in REGSAM samDesired, - __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, - __out PHKEY phkResult, - __out_opt LPDWORD lpdwDisposition - ); -typedef LSTATUS (APIENTRY *PFN_REGOPENKEYEXW)( - __in HKEY hKey, - __in_opt LPCWSTR lpSubKey, - __reserved DWORD ulOptions, - __in REGSAM samDesired, - __out PHKEY phkResult - ); -typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __in REGSAM samDesired, - __reserved DWORD Reserved - ); -typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)( - __in HKEY hKey, - __in LPCWSTR lpSubKey - ); -typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)( - __in HKEY hKey, - __in DWORD dwIndex, - __out LPWSTR lpName, - __inout LPDWORD lpcName, - __reserved LPDWORD lpReserved, - __inout_opt LPWSTR lpClass, - __inout_opt LPDWORD lpcClass, - __out_opt PFILETIME lpftLastWriteTime - ); -typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)( - __in HKEY hKey, - __in DWORD dwIndex, - __out LPWSTR lpValueName, - __inout LPDWORD lpcchValueName, - __reserved LPDWORD lpReserved, - __out_opt LPDWORD lpType, - __out_opt LPBYTE lpData, - __out_opt LPDWORD lpcbData - ); -typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)( - __in HKEY hKey, - __out_opt LPWSTR lpClass, - __inout_opt LPDWORD lpcClass, - __reserved LPDWORD lpReserved, - __out_opt LPDWORD lpcSubKeys, - __out_opt LPDWORD lpcMaxSubKeyLen, - __out_opt LPDWORD lpcMaxClassLen, - __out_opt LPDWORD lpcValues, - __out_opt LPDWORD lpcMaxValueNameLen, - __out_opt LPDWORD lpcMaxValueLen, - __out_opt LPDWORD lpcbSecurityDescriptor, - __out_opt PFILETIME lpftLastWriteTime - ); -typedef LSTATUS (APIENTRY *PFN_REGQUERYVALUEEXW)( - __in HKEY hKey, - __in_opt LPCWSTR lpValueName, - __reserved LPDWORD lpReserved, - __out_opt LPDWORD lpType, - __out_bcount_part_opt(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData, - __inout_opt LPDWORD lpcbData - ); -typedef LSTATUS (APIENTRY *PFN_REGSETVALUEEXW)( - __in HKEY hKey, - __in_opt LPCWSTR lpValueName, - __reserved DWORD Reserved, - __in DWORD dwType, - __in_bcount_opt(cbData) CONST BYTE* lpData, - __in DWORD cbData - ); -typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)( - __in HKEY hKey, - __in_opt LPCWSTR lpValueName - ); - -HRESULT DAPI RegInitialize(); -void DAPI RegUninitialize(); - -void DAPI RegFunctionOverride( - __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, - __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, - __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, - __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, - __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, - __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, - __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, - __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, - __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW - ); -HRESULT DAPI RegCreate( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ); -HRESULT DAPI RegCreateEx( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __in BOOL fVolatile, - __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, - __out HKEY* phk, - __out_opt BOOL* pfCreated - ); -HRESULT DAPI RegOpen( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ); -HRESULT DAPI RegDelete( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fDeleteTree - ); -HRESULT DAPI RegKeyEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczKey - ); -HRESULT DAPI RegValueEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczName, - __out_opt DWORD *pdwType - ); -HRESULT DAPI RegGetType( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD *pdwType - ); -HRESULT DAPI RegReadBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, - __out SIZE_T *pcbBuffer - ); -HRESULT DAPI RegReadString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_z LPWSTR* psczValue - ); -HRESULT DAPI RegReadStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, - __out DWORD *pcStrings - ); -HRESULT DAPI RegReadVersion( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pdw64Version - ); -HRESULT DAPI RegReadNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD* pdwValue - ); -HRESULT DAPI RegReadQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pqwValue - ); -HRESULT DAPI RegWriteBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_bcount(cbBuffer) const BYTE *pbBuffer, - __in DWORD cbBuffer - ); -HRESULT DAPI RegWriteString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue - ); -HRESULT DAPI RegWriteStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_ecount(cStrings) LPWSTR *rgwzStrings, - __in DWORD cStrings - ); -HRESULT DAPI RegWriteStringFormatted( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in __format_string LPCWSTR szFormat, - ... - ); -HRESULT DAPI RegWriteNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD dwValue - ); -HRESULT DAPI RegWriteQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD64 qwValue - ); -HRESULT DAPI RegQueryKey( - __in HKEY hk, - __out_opt DWORD* pcSubKeys, - __out_opt DWORD* pcValues - ); -HRESULT DAPI RegKeyReadNumber( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit, - __out DWORD* pdwValue - ); -BOOL DAPI RegValueExists( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/resrutil.h b/src/dutil/inc/resrutil.h deleted file mode 100644 index 1f4d8e17..00000000 --- a/src/dutil/inc/resrutil.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI ResGetStringLangId( - __in_opt LPCWSTR wzPath, - __in UINT uID, - __out WORD *pwLangId - ); - -HRESULT DAPI ResReadString( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPWSTR* ppwzString - ); - -HRESULT DAPI ResReadStringAnsi( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPSTR* ppszString - ); - -HRESULT DAPI ResReadData( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szDataName, - __deref_out_bcount(*pcb) PVOID *ppv, - __out DWORD *pcb - ); - -HRESULT DAPI ResExportDataToFile( - __in_z LPCSTR szDataName, - __in_z LPCWSTR wzTargetFile, - __in DWORD dwCreationDisposition - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/reswutil.h b/src/dutil/inc/reswutil.h deleted file mode 100644 index 31435ae2..00000000 --- a/src/dutil/inc/reswutil.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI ResWriteString( - __in_z LPCWSTR wzResourceFile, - __in DWORD dwDataId, - __in_z LPCWSTR wzData, - __in WORD wLangId - ); - -HRESULT DAPI ResWriteData( - __in_z LPCWSTR wzResourceFile, - __in_z LPCSTR szDataName, - __in PVOID pData, - __in DWORD cbData - ); - -HRESULT DAPI ResImportDataFromFile( - __in_z LPCWSTR wzTargetFile, - __in_z LPCWSTR wzSourceFile, - __in_z LPCSTR szDataName - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/rexutil.h b/src/dutil/inc/rexutil.h deleted file mode 100644 index 77f5604a..00000000 --- a/src/dutil/inc/rexutil.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// defines -#define FILETABLESIZE 40 - -// structs -struct MEM_FILE -{ - LPCBYTE vpStart; - UINT uiCurrent; - UINT uiLength; -}; - -typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE; - -typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); -typedef VOID (*REX_CALLBACK_WRITE)(UINT cb); - - -struct FAKE_FILE // used __in internal file table -{ - BOOL fUsed; - FAKE_FILE_TYPE fftType; - MEM_FILE mfFile; // State for memory file - HANDLE hFile; // Handle for disk file -}; - -// functions -HRESULT RexInitialize(); -void RexUninitialize(); - -HRESULT RexExtract( - __in_z LPCSTR szResource, - __in_z LPCWSTR wzExtractId, - __in_z LPCWSTR wzExtractDir, - __in_z LPCWSTR wzExtractName, - __in REX_CALLBACK_PROGRESS pfnProgress, - __in REX_CALLBACK_WRITE pfnWrite, - __in LPVOID pvContext - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/rmutil.h b/src/dutil/inc/rmutil.h deleted file mode 100644 index ce7bf254..00000000 --- a/src/dutil/inc/rmutil.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _RMU_SESSION *PRMU_SESSION; - -HRESULT DAPI RmuJoinSession( - __out PRMU_SESSION *ppSession, - __in_z LPCWSTR wzSessionKey - ); - -HRESULT DAPI RmuAddFile( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzPath - ); - -HRESULT DAPI RmuAddProcessById( - __in PRMU_SESSION pSession, - __in DWORD dwProcessId - ); - -HRESULT DAPI RmuAddProcessesByName( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzProcessName - ); - -HRESULT DAPI RmuAddService( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzServiceName - ); - -HRESULT DAPI RmuRegisterResources( - __in PRMU_SESSION pSession - ); - -HRESULT DAPI RmuEndSession( - __in PRMU_SESSION pSession - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/rssutil.h b/src/dutil/inc/rssutil.h deleted file mode 100644 index 064ab147..00000000 --- a/src/dutil/inc/rssutil.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); } -#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; } - - -struct RSS_UNKNOWN_ATTRIBUTE -{ - LPWSTR wzNamespace; - LPWSTR wzAttribute; - LPWSTR wzValue; - - RSS_UNKNOWN_ATTRIBUTE* pNext; -}; - -struct RSS_UNKNOWN_ELEMENT -{ - LPWSTR wzNamespace; - LPWSTR wzElement; - LPWSTR wzValue; - - RSS_UNKNOWN_ATTRIBUTE* pAttributes; - RSS_UNKNOWN_ELEMENT* pNext; -}; - -struct RSS_ITEM -{ - LPWSTR wzTitle; - LPWSTR wzLink; - LPWSTR wzDescription; - - LPWSTR wzGuid; - FILETIME ftPublished; - - LPWSTR wzEnclosureUrl; - DWORD dwEnclosureSize; - LPWSTR wzEnclosureType; - - RSS_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct RSS_CHANNEL -{ - LPWSTR wzTitle; - LPWSTR wzLink; - LPWSTR wzDescription; - DWORD dwTimeToLive; - - RSS_UNKNOWN_ELEMENT* pUnknownElements; - - DWORD cItems; - RSS_ITEM rgItems[1]; -}; - -HRESULT DAPI RssInitialize( - ); - -void DAPI RssUninitialize( - ); - -HRESULT DAPI RssParseFromString( - __in_z LPCWSTR wzRssString, - __out RSS_CHANNEL **ppChannel - ); - -HRESULT DAPI RssParseFromFile( - __in_z LPCWSTR wzRssFile, - __out RSS_CHANNEL **ppChannel - ); - -// Adding this until we have the updated specstrings.h -#ifndef __in_xcount -#define __in_xcount(size) -#endif - -void DAPI RssFreeChannel( - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/sceutil.h b/src/dutil/inc/sceutil.h deleted file mode 100644 index 9d14eecf..00000000 --- a/src/dutil/inc/sceutil.h +++ /dev/null @@ -1,273 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -typedef void* SCE_DATABASE_HANDLE; -typedef void* SCE_ROW_HANDLE; -typedef void* SCE_QUERY_HANDLE; -typedef void* SCE_QUERY_RESULTS_HANDLE; - -extern const int SCE_ROW_HANDLE_BYTES; -extern const int SCE_QUERY_HANDLE_BYTES; -extern const int SCE_QUERY_RESULTS_HANDLE_BYTES; - -#define ReleaseSceRow(prrh) if (prrh) { SceFreeRow(prrh); } -#define ReleaseNullSceRow(prrh) if (prrh) { SceFreeRow(prrh); prrh = NULL; } -#define ReleaseSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); } -#define ReleaseNullSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); pqh = NULL; } -#define ReleaseSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); } -#define ReleaseNullSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); pqh = NULL; } - -struct SCE_COLUMN_SCHEMA -{ - LPCWSTR wzName; - DBTYPE dbtColumnType; - DWORD dwLength; - BOOL fPrimaryKey; // If this column is the primary key - BOOL fNullable; - BOOL fAutoIncrement; - BOOL fDescending; // If this column should be descending when used in an index (default is ascending) - - LPWSTR wzRelationName; - DWORD dwForeignKeyTable; - DWORD dwForeignKeyColumn; -}; - -struct SCE_INDEX_SCHEMA -{ - LPWSTR wzName; - - DWORD *rgColumns; - DWORD cColumns; -}; - -struct SCE_TABLE_SCHEMA -{ - LPCWSTR wzName; - DWORD cColumns; - SCE_COLUMN_SCHEMA *rgColumns; - - DWORD cIndexes; - SCE_INDEX_SCHEMA *rgIndexes; - - // Internal to SCEUtil - consumers shouldn't access or modify - // TODO: enforce / hide in a handle of some sort? - IRowset *pIRowset; - IRowsetChange *pIRowsetChange; -}; - -struct SCE_DATABASE_SCHEMA -{ - DWORD cTables; - SCE_TABLE_SCHEMA *rgTables; -}; - -struct SCE_DATABASE -{ - SCE_DATABASE_HANDLE sdbHandle; - SCE_DATABASE_SCHEMA *pdsSchema; -}; - -HRESULT DAPI SceCreateDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __deref_out SCE_DATABASE **ppDatabase - ); -HRESULT DAPI SceOpenDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzSchemaType, - __in DWORD dwExpectedVersion, - __deref_out SCE_DATABASE **ppDatabase, - __in BOOL fReadOnly - ); -HRESULT DAPI SceEnsureDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzSchemaType, - __in DWORD dwExpectedVersion, - __in SCE_DATABASE_SCHEMA *pdsSchema, - __deref_out SCE_DATABASE **ppDatabase - ); -HRESULT DAPI SceIsTableEmpty( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out BOOL *pfEmpty - ); -HRESULT DAPI SceGetFirstRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceGetNextRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceBeginTransaction( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceCommitTransaction( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceRollbackTransaction( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceDeleteRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI ScePrepareInsert( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceFinishUpdate( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle - ); -HRESULT DAPI SceSetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ); -HRESULT DAPI SceSetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD dwValue - ); -HRESULT DAPI SceSetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD64 qwValue - ); -HRESULT DAPI SceSetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const BOOL fValue - ); -HRESULT DAPI SceSetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_z_opt LPCWSTR wzValue - ); -HRESULT DAPI SceSetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const SYSTEMTIME *pst - ); -HRESULT DAPI SceSetColumnNull( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex - ); -HRESULT DAPI SceGetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbBuffer, - __inout SIZE_T *pcbBuffer - ); -HRESULT DAPI SceGetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out DWORD *pdwValue - ); -HRESULT DAPI SceGetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out DWORD64 *pqwValue - ); -HRESULT DAPI SceGetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out BOOL *pfValue - ); -HRESULT DAPI SceGetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_z LPWSTR *psczValue - ); -HRESULT DAPI SceGetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out SYSTEMTIME *pst - ); -HRESULT DAPI SceBeginQuery( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __in DWORD dwIndex, - __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle - ); -HRESULT DAPI SceSetQueryColumnBinary( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ); -HRESULT DAPI SceSetQueryColumnDword( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD dwValue - ); -HRESULT DAPI SceSetQueryColumnQword( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD64 qwValue - ); -HRESULT DAPI SceSetQueryColumnBool( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const BOOL fValue - ); -HRESULT DAPI SceSetQueryColumnString( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_z_opt LPCWSTR wzString - ); -HRESULT DAPI SceSetQueryColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in const SYSTEMTIME *pst - ); -HRESULT DAPI SceSetQueryColumnEmpty( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle - ); -HRESULT DAPI SceRunQueryExact( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceRunQueryRange( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle - ); -HRESULT DAPI SceGetNextResultRow( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -void DAPI SceCloseTable( - __in SCE_TABLE_SCHEMA *pTable - ); -// Returns whether the data in the database changed. Ignores schema changes. -BOOL DAPI SceDatabaseChanged( - __in SCE_DATABASE *pDatabase - ); -// Resets the database changed flag -void DAPI SceResetDatabaseChanged( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceCloseDatabase( - __in SCE_DATABASE *pDatabase - ); -void DAPI SceFreeRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle - ); -void DAPI SceFreeQuery( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle - ); -void DAPI SceFreeQueryResults( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/sczutil.h b/src/dutil/inc/sczutil.h deleted file mode 100644 index fcfbd13a..00000000 --- a/src/dutil/inc/sczutil.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -class PSCZ -{ -public: - PSCZ() : m_scz(NULL) { } - - ~PSCZ() { ReleaseNullStr(m_scz); } - - operator LPWSTR() { return m_scz; } - - operator LPCWSTR() { return m_scz; } - - operator bool() { return NULL != m_scz; } - - LPWSTR* operator &() { return &m_scz; } - - bool operator !() { return !m_scz; } - - WCHAR operator *() { return *m_scz; } - - LPWSTR Detach() { LPWSTR scz = m_scz; m_scz = NULL; return scz; } - -private: - LPWSTR m_scz; -}; -#endif //__cplusplus diff --git a/src/dutil/inc/shelutil.h b/src/dutil/inc/shelutil.h deleted file mode 100644 index 0b9f539d..00000000 --- a/src/dutil/inc/shelutil.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifndef REFKNOWNFOLDERID -#define REFKNOWNFOLDERID REFGUID -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)( - __inout LPSHELLEXECUTEINFOW lpExecInfo - ); - -void DAPI ShelFunctionOverride( - __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW - ); -HRESULT DAPI ShelExec( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd, - __in_opt HWND hwndParent, - __out_opt HANDLE* phProcess - ); -HRESULT DAPI ShelExecUnelevated( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd - ); -HRESULT DAPI ShelGetFolder( - __out_z LPWSTR* psczFolderPath, - __in int csidlFolder - ); -HRESULT DAPI ShelGetKnownFolder( - __out_z LPWSTR* psczFolderPath, - __in REFKNOWNFOLDERID rfidFolder - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/sqlutil.h b/src/dutil/inc/sqlutil.h deleted file mode 100644 index ddf09323..00000000 --- a/src/dutil/inc/sqlutil.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -// Adding this until the SQL annotations are published to specstrings.h -#ifndef __sql_command -#define __sql_command -#endif - -// structs -struct SQL_FILESPEC -{ - WCHAR wzName[MAX_PATH]; - WCHAR wzFilename[MAX_PATH]; - WCHAR wzSize[MAX_PATH]; - WCHAR wzMaxSize[MAX_PATH]; - WCHAR wzGrow[MAX_PATH]; -}; - - -// functions -HRESULT DAPI SqlConnectDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out IDBCreateSession** ppidbSession - ); -HRESULT DAPI SqlStartTransaction( - __in IDBCreateSession* pidbSession, - __out IDBCreateCommand** ppidbCommand, - __out ITransaction** ppit - ); -HRESULT DAPI SqlEndTransaction( - __in ITransaction* pit, - __in BOOL fCommit - ); -HRESULT DAPI SqlDatabaseExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionDatabaseExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlDatabaseEnsureExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionDatabaseEnsureExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlCreateDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionCreateDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlDropDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionDropDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionExecuteQuery( - __in IDBCreateSession* pidbSession, - __in __sql_command LPCWSTR wzSql, - __out_opt IRowset** ppirs, - __out_opt DBROWCOUNT* pcRows, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlCommandExecuteQuery( - __in IDBCreateCommand* pidbCommand, - __in __sql_command LPCWSTR wzSql, - __out IRowset** ppirs, - __out DBROWCOUNT* pcRows - ); -HRESULT DAPI SqlGetErrorInfo( - __in IUnknown* pObjectWithError, - __in REFIID IID_InterfaceWithError, - __in DWORD dwLocaleId, - __out_opt BSTR* pbstrErrorSource, - __out_opt BSTR* pbstrErrorDescription - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/srputil.h b/src/dutil/inc/srputil.h deleted file mode 100644 index 95e96231..00000000 --- a/src/dutil/inc/srputil.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef enum SRP_ACTION -{ - SRP_ACTION_UNKNOWN, - SRP_ACTION_UNINSTALL, - SRP_ACTION_INSTALL, - SRP_ACTION_MODIFY, -} SRP_ACTION; - - -/******************************************************************** - SrpInitialize - initializes system restore point functionality. - -*******************************************************************/ -DAPI_(HRESULT) SrpInitialize( - __in BOOL fInitializeComSecurity - ); - -/******************************************************************** - SrpUninitialize - uninitializes system restore point functionality. - -*******************************************************************/ -DAPI_(void) SrpUninitialize(); - -/******************************************************************** - SrpCreateRestorePoint - creates a system restore point. - -*******************************************************************/ -DAPI_(HRESULT) SrpCreateRestorePoint( - __in_z LPCWSTR wzApplicationName, - __in SRP_ACTION action - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h deleted file mode 100644 index 1cff9ab8..00000000 --- a/src/dutil/inc/strutil.h +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseStr(pwz) if (pwz) { StrFree(pwz); } -#define ReleaseNullStr(pwz) if (pwz) { StrFree(pwz); pwz = NULL; } -#define ReleaseBSTR(bstr) if (bstr) { ::SysFreeString(bstr); } -#define ReleaseNullBSTR(bstr) if (bstr) { ::SysFreeString(bstr); bstr = NULL; } -#define ReleaseStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); } } -#define ReleaseNullStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); c = 0; rg = NULL; } } -#define ReleaseNullStrSecure(pwz) if (pwz) { StrSecureZeroFreeString(pwz); pwz = NULL; } - -#define DeclareConstBSTR(bstr_const, wz) const WCHAR bstr_const[] = { 0x00, 0x00, sizeof(wz)-sizeof(WCHAR), 0x00, wz } -#define UseConstBSTR(bstr_const) const_cast(bstr_const + 4) - -HRESULT DAPI StrAlloc( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ); -HRESULT DAPI StrAllocSecure( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ); -HRESULT DAPI StrTrimCapacity( - __deref_out_z LPWSTR* ppwz - ); -HRESULT DAPI StrTrimWhitespace( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource - ); -HRESULT DAPI StrAnsiAlloc( - __deref_out_ecount_part(cch, 0) LPSTR* ppz, - __in SIZE_T cch - ); -HRESULT DAPI StrAnsiTrimCapacity( - __deref_out_z LPSTR* ppz - ); -HRESULT DAPI StrAnsiTrimWhitespace( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR szSource - ); -HRESULT DAPI StrAllocString( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocStringSecure( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAnsiAllocString( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ); -HRESULT DAPI StrAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ); -HRESULT DAPI StrAnsiAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocPrefix( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzPrefix, - __in SIZE_T cchPrefix - ); -HRESULT DAPI StrAllocConcat( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocConcatSecure( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAnsiAllocConcat( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR pzSource, - __in SIZE_T cchSource - ); -HRESULT __cdecl StrAllocFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAllocConcatFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAllocConcatFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAllocFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAnsiAllocFormatted( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - ... - ); -HRESULT DAPI StrAllocFormattedArgs( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ); -HRESULT DAPI StrAllocFormattedArgsSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ); -HRESULT DAPI StrAnsiAllocFormattedArgs( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - __in va_list args - ); -HRESULT DAPI StrAllocFromError( - __inout LPWSTR *ppwzMessage, - __in HRESULT hrError, - __in_opt HMODULE hModule, - ... - ); - -HRESULT DAPI StrMaxLength( - __in LPCVOID p, - __out SIZE_T* pcbch - ); -HRESULT DAPI StrSize( - __in LPCVOID p, - __out SIZE_T* pcbb - ); - -HRESULT DAPI StrFree( - __in LPVOID p - ); - - -HRESULT DAPI StrReplaceStringAll( - __inout LPWSTR* ppwzOriginal, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ); -HRESULT DAPI StrReplaceString( - __inout LPWSTR* ppwzOriginal, - __inout DWORD* pdwStartIndex, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ); - -HRESULT DAPI StrHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out_ecount(cchDest) LPWSTR wzDest, - __in SIZE_T cchDest - ); -HRESULT DAPI StrAllocHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest - ); -HRESULT DAPI StrHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(cbDest) BYTE* pbDest, - __in SIZE_T cbDest - ); -HRESULT DAPI StrAllocHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(*pcbDest) BYTE** ppbDest, - __out_opt DWORD* pcbDest - ); - -HRESULT DAPI StrAllocBase85Encode( - __in_bcount_opt(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_z LPWSTR* pwzDest - ); -HRESULT DAPI StrAllocBase85Decode( - __in_z LPCWSTR wzSource, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out SIZE_T* pcbDest -); - -HRESULT DAPI MultiSzLen( - __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, - __out SIZE_T* pcch - ); -HRESULT DAPI MultiSzPrepend( - __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in __nullnullterminated LPCWSTR pwzInsert - ); -HRESULT DAPI MultiSzFindSubstring( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzSubstring, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn - ); -HRESULT DAPI MultiSzFindString( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzString, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound - ); -HRESULT DAPI MultiSzRemoveString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex - ); -HRESULT DAPI MultiSzInsertString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzInsert - ); -HRESULT DAPI MultiSzReplaceString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzString - ); - -LPCWSTR DAPI wcsistr( - __in_z LPCWSTR wzString, - __in_z LPCWSTR wzCharSet - ); - -HRESULT DAPI StrStringToInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out SHORT* psOut - ); -HRESULT DAPI StrStringToUInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out USHORT* pusOut - ); -HRESULT DAPI StrStringToInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out INT* piOut - ); -HRESULT DAPI StrStringToUInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out UINT* puiOut - ); -HRESULT DAPI StrStringToInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out LONGLONG* pllOut - ); -HRESULT DAPI StrStringToUInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out ULONGLONG* pullOut - ); -void DAPI StrStringToUpper( - __inout_z LPWSTR wzIn - ); -void DAPI StrStringToLower( - __inout_z LPWSTR wzIn - ); -HRESULT DAPI StrAllocStringToUpperInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocStringToLowerInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); - -HRESULT DAPI StrArrayAllocString( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); - -HRESULT DAPI StrArrayFree( - __in_ecount(cStrArray) LPWSTR *rgsczStrArray, - __in UINT cStrArray - ); - -HRESULT DAPI StrSplitAllocArray( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzDelim - ); - -HRESULT DAPI StrSecureZeroString( - __in LPWSTR pwz - ); -HRESULT DAPI StrSecureZeroFreeString( - __in LPWSTR pwz - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/svcutil.h b/src/dutil/inc/svcutil.h deleted file mode 100644 index 80d6326c..00000000 --- a/src/dutil/inc/svcutil.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; } - - -HRESULT DAPI SvcQueryConfig( - __in SC_HANDLE sch, - __out QUERY_SERVICE_CONFIGW** ppConfig - ); - - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h deleted file mode 100644 index d3dd6d21..00000000 --- a/src/dutil/inc/thmutil.h +++ /dev/null @@ -1,765 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; } - -typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)( - __in_z LPCWSTR wzCondition, - __out BOOL* pf, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)( - __in_z LPCWSTR wzFormat, - __inout LPWSTR* psczOut, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)( - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)( - __in_z LPCWSTR wzVariable, - __in LONGLONG llValue, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)( - __in_z LPCWSTR wzVariable, - __inout LPWSTR* psczValue, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)( - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in BOOL fFormatted, - __in_opt LPVOID pvContext - ); - -typedef enum THEME_ACTION_TYPE -{ - THEME_ACTION_TYPE_BROWSE_DIRECTORY, - THEME_ACTION_TYPE_CHANGE_PAGE, - THEME_ACTION_TYPE_CLOSE_WINDOW, -} THEME_ACTION_TYPE; - -typedef enum THEME_CONTROL_DATA -{ - THEME_CONTROL_DATA_HOVER = 1, -} THEME_CONTROL_DATA; - -typedef enum THEME_CONTROL_TYPE -{ - THEME_CONTROL_TYPE_UNKNOWN, - THEME_CONTROL_TYPE_BILLBOARD, - THEME_CONTROL_TYPE_BUTTON, - THEME_CONTROL_TYPE_CHECKBOX, - THEME_CONTROL_TYPE_COMBOBOX, - THEME_CONTROL_TYPE_COMMANDLINK, - THEME_CONTROL_TYPE_EDITBOX, - THEME_CONTROL_TYPE_HYPERLINK, - THEME_CONTROL_TYPE_HYPERTEXT, - THEME_CONTROL_TYPE_IMAGE, - THEME_CONTROL_TYPE_LABEL, - THEME_CONTROL_TYPE_PANEL, - THEME_CONTROL_TYPE_PROGRESSBAR, - THEME_CONTROL_TYPE_RADIOBUTTON, - THEME_CONTROL_TYPE_RICHEDIT, - THEME_CONTROL_TYPE_STATIC, - THEME_CONTROL_TYPE_LISTVIEW, - THEME_CONTROL_TYPE_TREEVIEW, - THEME_CONTROL_TYPE_TAB, -} THEME_CONTROL_TYPE; - -typedef enum THEME_SHOW_PAGE_REASON -{ - THEME_SHOW_PAGE_REASON_DEFAULT, - THEME_SHOW_PAGE_REASON_CANCEL, - THEME_SHOW_PAGE_REASON_REFRESH, -} THEME_SHOW_PAGE_REASON; - -typedef enum THEME_WINDOW_INITIAL_POSITION -{ - THEME_WINDOW_INITIAL_POSITION_DEFAULT, - THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, -} THEME_WINDOW_INITIAL_POSITION; - - -struct THEME_COLUMN -{ - LPWSTR pszName; - UINT uStringId; - int nDefaultDpiBaseWidth; - int nBaseWidth; - int nWidth; - BOOL fExpands; -}; - - -struct THEME_TAB -{ - LPWSTR pszName; - UINT uStringId; -}; - -struct THEME_ACTION -{ - LPWSTR sczCondition; - THEME_ACTION_TYPE type; - union - { - struct - { - LPWSTR sczVariableName; - } BrowseDirectory; - struct - { - LPWSTR sczPageName; - BOOL fCancel; - } ChangePage; - }; -}; - -struct THEME_CONDITIONAL_TEXT -{ - LPWSTR sczCondition; - LPWSTR sczText; -}; - -// THEME_ASSIGN_CONTROL_ID - Used to apply a specific id to a named control (usually -// to set the WM_COMMAND). -struct THEME_ASSIGN_CONTROL_ID -{ - WORD wId; // id to apply to control - LPCWSTR wzName; // name of control to match -}; - -const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned. - -struct THEME_CONTROL -{ - THEME_CONTROL_TYPE type; - - WORD wId; - WORD wPageId; - - LPWSTR sczName; // optional name for control, used to apply control id and link the control to a variable. - LPWSTR sczText; - LPWSTR sczTooltip; - LPWSTR sczNote; // optional text for command link - int nDefaultDpiX; - int nDefaultDpiY; - int nDefaultDpiHeight; - int nDefaultDpiWidth; - int nX; - int nY; - int nHeight; - int nWidth; - int nSourceX; - int nSourceY; - UINT uStringId; - - LPWSTR sczEnableCondition; - LPWSTR sczVisibleCondition; - BOOL fDisableVariableFunctionality; - - HBITMAP hImage; - HICON hIcon; - - // Don't free these; it's just a handle to the central image lists stored in THEME. The handle is freed once, there. - HIMAGELIST rghImageList[4]; - - DWORD dwStyle; - DWORD dwExtendedStyle; - DWORD dwInternalStyle; - - DWORD dwFontId; - - // child controls - DWORD cControls; - THEME_CONTROL* rgControls; - - // Used by billboard controls - WORD wBillboardInterval; - BOOL fBillboardLoops; - - // Used by button and command link controls - THEME_ACTION* rgActions; - DWORD cActions; - THEME_ACTION* pDefaultAction; - - // Used by hyperlink and owner-drawn button controls - DWORD dwFontHoverId; - DWORD dwFontSelectedId; - - // Used by listview controls - THEME_COLUMN *ptcColumns; - DWORD cColumns; - - // Used by radio button controls - BOOL fLastRadioButton; - LPWSTR sczValue; - LPWSTR sczVariable; - - // Used by tab controls - THEME_TAB *pttTabs; - DWORD cTabs; - - // Used by controls that have text - DWORD cConditionalText; - THEME_CONDITIONAL_TEXT* rgConditionalText; - - // Used by command link controls - DWORD cConditionalNotes; - THEME_CONDITIONAL_TEXT* rgConditionalNotes; - - // state variables that should be ignored - HWND hWnd; - DWORD dwData; // type specific data -}; - - -struct THEME_IMAGELIST -{ - LPWSTR sczName; - - HIMAGELIST hImageList; -}; - -struct THEME_SAVEDVARIABLE -{ - LPWSTR wzName; - LPWSTR sczValue; -}; - -struct THEME_PAGE -{ - WORD wId; - LPWSTR sczName; - - DWORD cControlIndices; - - DWORD cSavedVariables; - THEME_SAVEDVARIABLE* rgSavedVariables; -}; - -struct THEME_FONT_INSTANCE -{ - UINT nDpi; - HFONT hFont; -}; - -struct THEME_FONT -{ - LONG lfHeight; - LONG lfWeight; - BYTE lfUnderline; - BYTE lfQuality; - LPWSTR sczFaceName; - - COLORREF crForeground; - HBRUSH hForeground; - COLORREF crBackground; - HBRUSH hBackground; - - DWORD cFontInstances; - THEME_FONT_INSTANCE* rgFontInstances; -}; - - -struct THEME -{ - WORD wId; - - BOOL fAutoResize; - BOOL fForceResize; - - DWORD dwStyle; - DWORD dwFontId; - HANDLE hIcon; - LPWSTR sczCaption; - int nDefaultDpiHeight; - int nDefaultDpiMinimumHeight; - int nDefaultDpiWidth; - int nDefaultDpiMinimumWidth; - int nHeight; - int nMinimumHeight; - int nWidth; - int nMinimumWidth; - int nWindowHeight; - int nWindowWidth; - int nSourceX; - int nSourceY; - UINT uStringId; - - HBITMAP hImage; - - DWORD cFonts; - THEME_FONT* rgFonts; - - DWORD cPages; - THEME_PAGE* rgPages; - - DWORD cImageLists; - THEME_IMAGELIST* rgImageLists; - - DWORD cControls; - THEME_CONTROL* rgControls; - - // internal state variables -- do not use outside ThmUtil.cpp - HWND hwndParent; // parent for loaded controls - HWND hwndHover; // current hwnd hovered over - DWORD dwCurrentPageId; - HWND hwndTooltip; - - UINT nDpi; - - // callback functions - PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition; - PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString; - PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable; - PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable; - PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable; - PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable; - - LPVOID pvVariableContext; -}; - - -/******************************************************************** - ThemeInitialize - initialized theme management. - -*******************************************************************/ -HRESULT DAPI ThemeInitialize( - __in_opt HMODULE hModule - ); - -/******************************************************************** - ThemeUninitialize - uninitialize theme management. - -*******************************************************************/ -void DAPI ThemeUninitialize(); - -/******************************************************************** - ThemeLoadFromFile - loads a theme from a loose file. - - *******************************************************************/ -HRESULT DAPI ThemeLoadFromFile( - __in_z LPCWSTR wzThemeFile, - __out THEME** ppTheme - ); - -/******************************************************************** - ThemeLoadFromResource - loads a theme from a module's data resource. - - NOTE: The resource data must be UTF-8 encoded. -*******************************************************************/ -HRESULT DAPI ThemeLoadFromResource( - __in_opt HMODULE hModule, - __in_z LPCSTR szResource, - __out THEME** ppTheme - ); - -/******************************************************************** - ThemeFree - frees any memory associated with a theme. - -*******************************************************************/ -void DAPI ThemeFree( - __in THEME* pTheme - ); - -/******************************************************************** -ThemeRegisterVariableCallbacks - registers a context and callbacks - for working with variables. - -*******************************************************************/ -HRESULT DAPI ThemeRegisterVariableCallbacks( - __in THEME* pTheme, - __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, - __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, - __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, - __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, - __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, - __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, - __in_opt LPVOID pvContext - ); - -/******************************************************************** - ThemeCreateParentWindow - creates a parent window for the theme. - -*******************************************************************/ -HRESULT DAPI ThemeCreateParentWindow( - __in THEME* pTheme, - __in DWORD dwExStyle, - __in LPCWSTR szClassName, - __in LPCWSTR szWindowName, - __in DWORD dwStyle, - __in int x, - __in int y, - __in_opt HWND hwndParent, - __in_opt HINSTANCE hInstance, - __in_opt LPVOID lpParam, - __in THEME_WINDOW_INITIAL_POSITION initialPosition, - __out_opt HWND* phWnd - ); - -/******************************************************************** - ThemeLoadControls - creates the windows for all the theme controls - using the window created in ThemeCreateParentWindow. - -*******************************************************************/ -HRESULT DAPI ThemeLoadControls( - __in THEME* pTheme, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ); - -/******************************************************************** - ThemeUnloadControls - resets all the theme control windows so the theme - controls can be reloaded. - -*******************************************************************/ -void DAPI ThemeUnloadControls( - __in THEME* pTheme - ); - -/******************************************************************** - ThemeLocalize - Localizes all of the strings in the theme. - -*******************************************************************/ -HRESULT DAPI ThemeLocalize( - __in THEME *pTheme, - __in const WIX_LOCALIZATION *pLocStringSet - ); - -HRESULT DAPI ThemeLoadStrings( - __in THEME* pTheme, - __in HMODULE hResModule - ); - -/******************************************************************** - ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file. - - *******************************************************************/ -HRESULT DAPI ThemeLoadRichEditFromFile( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCWSTR wzFileName, - __in HMODULE hModule - ); - -/******************************************************************** - ThemeLoadRichEditFromResource - Attach a richedit control to resource data. - - *******************************************************************/ -HRESULT DAPI ThemeLoadRichEditFromResource( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ); - -/******************************************************************** - ThemeLoadRichEditFromResourceToHWnd - Attach a richedit control (by - HWND) to resource data. - - *******************************************************************/ -HRESULT DAPI ThemeLoadRichEditFromResourceToHWnd( - __in HWND hWnd, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ); - -/******************************************************************** - ThemeHandleKeyboardMessage - will translate the message using the active - accelerator table. - -*******************************************************************/ -BOOL DAPI ThemeHandleKeyboardMessage( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in MSG* pMsg - ); - -/******************************************************************** - ThemeDefWindowProc - replacement for DefWindowProc() when using theme. - -*******************************************************************/ -LRESULT CALLBACK ThemeDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); - -/******************************************************************** - ThemeGetPageIds - gets the page ids for the theme via page names. - -*******************************************************************/ -void DAPI ThemeGetPageIds( - __in const THEME* pTheme, - __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, - __inout_ecount(cGetPages) DWORD* rgdwPageIds, - __in DWORD cGetPages - ); - -/******************************************************************** - ThemeGetPage - gets a theme page by id. - - *******************************************************************/ -THEME_PAGE* DAPI ThemeGetPage( - __in const THEME* pTheme, - __in DWORD dwPage - ); - -/******************************************************************** - ThemeShowPage - shows or hides all of the controls in the page at one time. - - *******************************************************************/ -HRESULT DAPI ThemeShowPage( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow - ); - -/******************************************************************** -ThemeShowPageEx - shows or hides all of the controls in the page at one time. - When using variables, TSPR_CANCEL reverts any changes made. - TSPR_REFRESH forces reevaluation of conditions. - It is expected that the current page is hidden before - showing a new page. - -*******************************************************************/ -HRESULT DAPI ThemeShowPageEx( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow, - __in THEME_SHOW_PAGE_REASON reason - ); - - -/******************************************************************** -ThemeShowChild - shows a control's specified child control, hiding the rest. - -*******************************************************************/ -void DAPI ThemeShowChild( - __in THEME* pTheme, - __in THEME_CONTROL* pParentControl, - __in DWORD dwIndex - ); - -/******************************************************************** - ThemeControlExists - check if a control with the specified id exists. - - *******************************************************************/ -BOOL DAPI ThemeControlExists( - __in const THEME* pTheme, - __in DWORD dwControl - ); - -/******************************************************************** - ThemeControlEnable - enables/disables a control. - - *******************************************************************/ -void DAPI ThemeControlEnable( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fEnable - ); - -/******************************************************************** - ThemeControlEnabled - returns whether a control is enabled/disabled. - - *******************************************************************/ -BOOL DAPI ThemeControlEnabled( - __in THEME* pTheme, - __in DWORD dwControl - ); - -/******************************************************************** - ThemeControlElevates - sets/removes the shield icon on a control. - - *******************************************************************/ -void DAPI ThemeControlElevates( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fElevates - ); - -/******************************************************************** - ThemeShowControl - shows/hides a control. - - *******************************************************************/ -void DAPI ThemeShowControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ); - -/******************************************************************** -ThemeShowControlEx - shows/hides a control with support for -conditional text and notes. - -*******************************************************************/ -void DAPI ThemeShowControlEx( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ); - -/******************************************************************** - ThemeControlVisible - returns whether a control is visible. - - *******************************************************************/ -BOOL DAPI ThemeControlVisible( - __in THEME* pTheme, - __in DWORD dwControl - ); - -BOOL DAPI ThemePostControlMessage( - __in THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ); - -LRESULT DAPI ThemeSendControlMessage( - __in const THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ); - -/******************************************************************** - ThemeDrawBackground - draws the theme background. - -*******************************************************************/ -HRESULT DAPI ThemeDrawBackground( - __in THEME* pTheme, - __in PAINTSTRUCT* pps - ); - -/******************************************************************** - ThemeDrawControl - draw an owner drawn control. - -*******************************************************************/ -HRESULT DAPI ThemeDrawControl( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis - ); - -/******************************************************************** - ThemeHoverControl - mark a control as hover. - -*******************************************************************/ -BOOL DAPI ThemeHoverControl( - __in THEME* pTheme, - __in HWND hwndParent, - __in HWND hwndControl - ); - -/******************************************************************** - ThemeIsControlChecked - gets whether a control is checked. Only - really useful for checkbox controls. - -*******************************************************************/ -BOOL DAPI ThemeIsControlChecked( - __in THEME* pTheme, - __in DWORD dwControl - ); - -/******************************************************************** - ThemeSetControlColor - sets the color of text for a control. - -*******************************************************************/ -BOOL DAPI ThemeSetControlColor( - __in THEME* pTheme, - __in HDC hdc, - __in HWND hWnd, - __out HBRUSH* phBackgroundBrush - ); - -/******************************************************************** - ThemeSetProgressControl - sets the current percentage complete in a - progress bar control. - -*******************************************************************/ -HRESULT DAPI ThemeSetProgressControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwProgressPercentage - ); - -/******************************************************************** - ThemeSetProgressControlColor - sets the current color of a - progress bar control. - -*******************************************************************/ -HRESULT DAPI ThemeSetProgressControlColor( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwColorIndex - ); - -/******************************************************************** - ThemeSetTextControl - sets the text of a control. - -*******************************************************************/ -HRESULT DAPI ThemeSetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __in_z_opt LPCWSTR wzText - ); - -/******************************************************************** -ThemeSetTextControl - sets the text of a control and optionally - invalidates the control. - -*******************************************************************/ -HRESULT DAPI ThemeSetTextControlEx( - __in const THEME* pTheme, - __in DWORD dwControl, - __in BOOL fUpdate, - __in_z_opt LPCWSTR wzText - ); - -/******************************************************************** - ThemeGetTextControl - gets the text of a control. - -*******************************************************************/ -HRESULT DAPI ThemeGetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __inout_z LPWSTR* psczText - ); - -/******************************************************************** - ThemeUpdateCaption - updates the caption in the theme. - -*******************************************************************/ -HRESULT DAPI ThemeUpdateCaption( - __in THEME* pTheme, - __in_z LPCWSTR wzCaption - ); - -/******************************************************************** - ThemeSetFocus - set the focus to the control supplied or the next - enabled control if it is disabled. - -*******************************************************************/ -void DAPI ThemeSetFocus( - __in THEME* pTheme, - __in DWORD dwControl - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/timeutil.h b/src/dutil/inc/timeutil.h deleted file mode 100644 index 3655c00a..00000000 --- a/src/dutil/inc/timeutil.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI TimeFromString( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ); -HRESULT DAPI TimeFromString3339( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ); -HRESULT DAPI TimeCurrentTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ); -HRESULT DAPI TimeCurrentDateTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ); -HRESULT DAPI TimeSystemDateTime( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME *pst, - __in BOOL fGMT - ); -HRESULT DAPI TimeSystemToDateTimeString( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME *pst, - __in LCID locale - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/uncutil.h b/src/dutil/inc/uncutil.h deleted file mode 100644 index 6516a801..00000000 --- a/src/dutil/inc/uncutil.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -/******************************************************************* - UncConvertFromMountedDrive - Converts the string in-place from a - mounted drive path to a UNC path -*******************************************************************/ -DAPI_(HRESULT) UncConvertFromMountedDrive( - __inout LPWSTR *psczUNCPath, - __in LPCWSTR sczMountedDrivePath - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/uriutil.h b/src/dutil/inc/uriutil.h deleted file mode 100644 index d6dfdd6b..00000000 --- a/src/dutil/inc/uriutil.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include "wininet.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum URI_PROTOCOL -{ - URI_PROTOCOL_UNKNOWN, - URI_PROTOCOL_FILE, - URI_PROTOCOL_FTP, - URI_PROTOCOL_HTTP, - URI_PROTOCOL_HTTPS, - URI_PROTOCOL_LOCAL, - URI_PROTOCOL_UNC -} URI_PROTOCOL; - -typedef struct _URI_INFO -{ - INTERNET_SCHEME scheme; - LPWSTR sczHostName; - INTERNET_PORT port; - LPWSTR sczUser; - LPWSTR sczPassword; - LPWSTR sczPath; - LPWSTR sczQueryString; -} URI_INFO; - - -HRESULT DAPI UriCanonicalize( - __inout_z LPWSTR* psczUri - ); - -HRESULT DAPI UriCrack( - __in_z LPCWSTR wzUri, - __out_opt INTERNET_SCHEME* pScheme, - __deref_opt_out_z LPWSTR* psczHostName, - __out_opt INTERNET_PORT* pPort, - __deref_opt_out_z LPWSTR* psczUser, - __deref_opt_out_z LPWSTR* psczPassword, - __deref_opt_out_z LPWSTR* psczPath, - __deref_opt_out_z LPWSTR* psczQueryString - ); - -HRESULT DAPI UriCrackEx( - __in_z LPCWSTR wzUri, - __in URI_INFO* pUriInfo - ); - -void DAPI UriInfoUninitialize( - __in URI_INFO* pUriInfo - ); - -HRESULT DAPI UriCreate( - __inout_z LPWSTR* psczUri, - __in INTERNET_SCHEME scheme, - __in_z_opt LPWSTR wzHostName, - __in INTERNET_PORT port, - __in_z_opt LPWSTR wzUser, - __in_z_opt LPWSTR wzPassword, - __in_z_opt LPWSTR wzPath, - __in_z_opt LPWSTR wzQueryString - ); - -HRESULT DAPI UriCanonicalize( - __inout_z LPWSTR* psczUri - ); - -HRESULT DAPI UriFile( - __deref_out_z LPWSTR* psczFile, - __in_z LPCWSTR wzUri - ); - -HRESULT DAPI UriProtocol( - __in_z LPCWSTR wzUri, - __out URI_PROTOCOL* pProtocol - ); - -HRESULT DAPI UriRoot( - __in_z LPCWSTR wzUri, - __out LPWSTR* ppwzRoot, - __out_opt URI_PROTOCOL* pProtocol - ); - -HRESULT DAPI UriResolve( - __in_z LPCWSTR wzUri, - __in_opt LPCWSTR wzBaseUri, - __out LPWSTR* ppwzResolvedUri, - __out_opt URI_PROTOCOL* pResolvedProtocol - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/userutil.h b/src/dutil/inc/userutil.h deleted file mode 100644 index 2c86d229..00000000 --- a/src/dutil/inc/userutil.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI UserBuildDomainUserName( - __out_ecount_z(cchDest) LPWSTR wzDest, - __in int cchDest, - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain - ); - -HRESULT DAPI UserCheckIsMember( - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain, - __in_z LPCWSTR pwzGroupName, - __in_z LPCWSTR pwzGroupDomain, - __out LPBOOL lpfMember - ); - -HRESULT DAPI UserCreateADsPath( - __in_z LPCWSTR wzObjectDomain, - __in_z LPCWSTR wzObjectName, - __out BSTR *pbstrAdsPath - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/verutil.h b/src/dutil/inc/verutil.h deleted file mode 100644 index 5247bb61..00000000 --- a/src/dutil/inc/verutil.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; } - -typedef struct _VERUTIL_VERSION_RELEASE_LABEL -{ - BOOL fNumeric; - DWORD dwValue; - SIZE_T cchLabelOffset; - int cchLabel; -} VERUTIL_VERSION_RELEASE_LABEL; - -typedef struct _VERUTIL_VERSION -{ - LPWSTR sczVersion; - DWORD dwMajor; - DWORD dwMinor; - DWORD dwPatch; - DWORD dwRevision; - DWORD cReleaseLabels; - VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels; - SIZE_T cchMetadataOffset; - BOOL fInvalid; -} VERUTIL_VERSION; - -/******************************************************************* - VerCompareParsedVersions - compares the Verutil versions. - -*******************************************************************/ -HRESULT DAPI VerCompareParsedVersions( - __in_opt VERUTIL_VERSION* pVersion1, - __in_opt VERUTIL_VERSION* pVersion2, - __out int* pnResult - ); - -/******************************************************************* - VerCompareStringVersions - parses the strings with VerParseVersion and then - compares the Verutil versions with VerCompareParsedVersions. - -*******************************************************************/ -HRESULT DAPI VerCompareStringVersions( - __in_z LPCWSTR wzVersion1, - __in_z LPCWSTR wzVersion2, - __in BOOL fStrict, - __out int* pnResult - ); - -/******************************************************************** - VerCopyVersion - copies the given Verutil version. - -*******************************************************************/ -HRESULT DAPI VerCopyVersion( - __in VERUTIL_VERSION* pSource, - __out VERUTIL_VERSION** ppVersion - ); - -/******************************************************************** - VerFreeVersion - frees any memory associated with a Verutil version. - -*******************************************************************/ -void DAPI VerFreeVersion( - __in VERUTIL_VERSION* pVersion - ); - -/******************************************************************* - VerParseVersion - parses the string into a Verutil version. - -*******************************************************************/ -HRESULT DAPI VerParseVersion( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __in BOOL fStrict, - __out VERUTIL_VERSION** ppVersion - ); - -/******************************************************************* - VerParseVersion - parses the QWORD into a Verutil version. - -*******************************************************************/ -HRESULT DAPI VerVersionFromQword( - __in DWORD64 qwVersion, - __out VERUTIL_VERSION** ppVersion - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/wiutil.h b/src/dutil/inc/wiutil.h deleted file mode 100644 index 9c2de209..00000000 --- a/src/dutil/inc/wiutil.h +++ /dev/null @@ -1,402 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// constants - -#define IDNOACTION 0 -#define WIU_MB_OKIGNORECANCELRETRY 0xE - -#define MAX_DARWIN_KEY 73 -#define MAX_DARWIN_COLUMN 255 - -#define WIU_LOG_DEFAULT INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | \ - INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_RESOLVESOURCE | \ - INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | \ - INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROPERTYDUMP - -#define ReleaseMsi(h) if (h) { ::MsiCloseHandle(h); } -#define ReleaseNullMsi(h) if (h) { ::MsiCloseHandle(h); h = NULL; } - - -typedef enum WIU_RESTART -{ - WIU_RESTART_NONE, - WIU_RESTART_REQUIRED, - WIU_RESTART_INITIATED, -} WIU_RESTART; - -typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE -{ - WIU_MSI_EXECUTE_MESSAGE_NONE, - WIU_MSI_EXECUTE_MESSAGE_PROGRESS, - WIU_MSI_EXECUTE_MESSAGE_ERROR, - WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE, - WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE, -} WIU_MSI_EXECUTE_MESSAGE_TYPE; - - -// structures - -typedef struct _WIU_MSI_EXECUTE_MESSAGE -{ - WIU_MSI_EXECUTE_MESSAGE_TYPE type; - DWORD dwAllowedResults; - - DWORD cData; - LPCWSTR* rgwzData; - - INT nResultRecommendation; // recommended return result for this message based on analysis of real world installs. - - union - { - struct - { - DWORD dwPercentage; - } progress; - struct - { - DWORD dwErrorCode; - LPCWSTR wzMessage; - } error; - struct - { - INSTALLMESSAGE mt; - LPCWSTR wzMessage; - } msiMessage; - struct - { - DWORD cFiles; - LPCWSTR* rgwzFiles; - } msiFilesInUse; - }; -} WIU_MSI_EXECUTE_MESSAGE; - -typedef struct _WIU_MSI_PROGRESS -{ - DWORD dwTotal; - DWORD dwCompleted; - DWORD dwStep; - BOOL fMoveForward; - BOOL fEnableActionData; - BOOL fScriptInProgress; -} WIU_MSI_PROGRESS; - - -typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)( - __in WIU_MSI_EXECUTE_MESSAGE* pMessage, - __in_opt LPVOID pvContext - ); - -typedef struct _WIU_MSI_EXECUTE_CONTEXT -{ - BOOL fRollback; - PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; - LPVOID pvContext; - WIU_MSI_PROGRESS rgMsiProgress[64]; - DWORD dwCurrentProgressIndex; - - INSTALLUILEVEL previousInstallUILevel; - HWND hwndPreviousParentWindow; - INSTALLUI_HANDLERW pfnPreviousExternalUI; - INSTALLUI_HANDLER_RECORD pfnPreviousExternalUIRecord; - - BOOL fSetPreviousExternalUIRecord; - BOOL fSetPreviousExternalUI; -} WIU_MSI_EXECUTE_CONTEXT; - - -// typedefs -typedef UINT (WINAPI *PFN_MSIENABLELOGW)( - __in DWORD dwLogMode, - __in_z LPCWSTR szLogFile, - __in DWORD dwLogAttributes - ); -typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)( - __in LPCWSTR szProductCode, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout LPDWORD pcchValue - ); -typedef INSTALLSTATE (WINAPI *PFN_MSIGETCOMPONENTPATHW)( - __in LPCWSTR szProduct, - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ); -typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)( - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ); -typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOEXW)( - __in LPCWSTR szProductCode, - __in_opt LPCWSTR szUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout_opt LPDWORD pcchValue - ); -typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)( - __in LPCWSTR szProduct, - __in LPCWSTR szFeature - ); -typedef UINT (WINAPI *PFN_MSIGETPATCHINFOEXW)( - __in_z LPCWSTR wzPatchCode, - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out_opt LPWSTR wzValue, - __inout DWORD* pcchValue - ); -typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT context, - __in DWORD cPatchInfo, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo - ); -typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)( - __in_z LPCWSTR wzProductPackagePath, - __in DWORD cPatchInfo, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo - ); -typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)( - __in LPCWSTR szPackagePath, - __in_opt LPCWSTR szCommandLine - ); -typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)( - __in LPCWSTR szProduct, - __in int iInstallLevel, - __in INSTALLSTATE eInstallState, - __in_opt LPCWSTR szCommandLine - ); -typedef UINT (WINAPI *PFN_MSIREMOVEPATCHESW)( - __in_z LPCWSTR wzPatchList, - __in_z LPCWSTR wzProductCode, - __in INSTALLTYPE eUninstallType, - __in_z_opt LPCWSTR szPropertyList - ); -typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)( - __in INSTALLUILEVEL dwUILevel, - __inout_opt HWND *phWnd - ); -typedef UINT (WINAPI *PFN_MSISETEXTERNALUIRECORD)( - __in_opt INSTALLUI_HANDLER_RECORD puiHandler, - __in DWORD dwMessageFilter, - __in_opt LPVOID pvContext, - __out_opt PINSTALLUI_HANDLER_RECORD ppuiPrevHandler - ); -typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)( - __in_opt INSTALLUI_HANDLERW puiHandler, - __in DWORD dwMessageFilter, - __in_opt LPVOID pvContext - ); -typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)( - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf - ); -typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSEXW)( - __in_z_opt LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in DWORD dwContext, - __in DWORD dwIndex, - __out_opt WCHAR wzInstalledProductCode[39], - __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, - __out_opt LPWSTR wzSid, - __inout_opt LPDWORD pcchSid - ); - -typedef UINT (WINAPI *PFN_MSIENUMRELATEDPRODUCTSW)( - __in LPCWSTR lpUpgradeCode, - __reserved DWORD dwReserved, - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf - ); -typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)( - __in LPCWSTR szProductCodeOrPatchCode, - __in_opt LPCWSTR szUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in DWORD dwOptions, - __in LPCWSTR szSource, - __in_opt DWORD dwIndex - ); -typedef UINT(WINAPI* PFN_MSIBEGINTRANSACTIONW)( - __in LPCWSTR szName, - __in DWORD dwTransactionAttributes, - __out MSIHANDLE* phTransactionHandle, - __out HANDLE* phChangeOfOwnerEvent - ); -typedef UINT(WINAPI* PFN_MSIENDTRANSACTION)( - __in DWORD dwTransactionState - ); - - -HRESULT DAPI WiuInitialize( - ); -void DAPI WiuUninitialize( - ); -void DAPI WiuFunctionOverride( - __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, - __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, - __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, - __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, - __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, - __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, - __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, - __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, - __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, - __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, - __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, - __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, - __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW - ); -HRESULT DAPI WiuGetComponentPath( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ); -HRESULT DAPI WiuLocateComponent( - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ); -HRESULT DAPI WiuQueryFeatureState( - __in_z LPCWSTR wzProduct, - __in_z LPCWSTR wzFeature, - __out INSTALLSTATE* pInstallState - ); -HRESULT DAPI WiuGetProductInfo( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuGetProductInfoEx( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuGetProductProperty( - __in MSIHANDLE hProduct, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuGetPatchInfoEx( - __in_z LPCWSTR wzPatchCode, - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuDeterminePatchSequence( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT context, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ); -HRESULT DAPI WiuDetermineApplicablePatches( - __in_z LPCWSTR wzProductPackagePath, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ); -HRESULT DAPI WiuEnumProducts( - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ); -HRESULT DAPI WiuEnumProductsEx( - __in_z_opt LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in DWORD dwContext, - __in DWORD dwIndex, - __out_opt WCHAR wzInstalledProductCode[39], - __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, - __out_opt LPWSTR wzSid, - __inout_opt LPDWORD pcchSid - ); -HRESULT DAPI WiuEnumRelatedProducts( - __in_z LPCWSTR wzUpgradeCode, - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ); -HRESULT DAPI WiuEnumRelatedProductCodes( - __in_z LPCWSTR wzUpgradeCode, - __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, - __out DWORD* pcRelatedProducts, - __in BOOL fReturnHighestVersionOnly - ); -HRESULT DAPI WiuEnableLog( - __in DWORD dwLogMode, - __in_z LPCWSTR wzLogFile, - __in DWORD dwLogAttributes - ); -HRESULT DAPI WiuInitializeInternalUI( - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ); -HRESULT DAPI WiuInitializeExternalUI( - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in LPVOID pvContext, - __in BOOL fRollback, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ); -void DAPI WiuUninitializeExternalUI( - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ); -HRESULT DAPI WiuConfigureProductEx( - __in_z LPCWSTR wzProduct, - __in int iInstallLevel, - __in INSTALLSTATE eInstallState, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ); -HRESULT DAPI WiuInstallProduct( - __in_z LPCWSTR wzPackagPath, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ); -HRESULT DAPI WiuRemovePatches( - __in_z LPCWSTR wzPatchList, - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzPropertyList, - __out WIU_RESTART* pRestart - ); -HRESULT DAPI WiuSourceListAddSourceEx( - __in_z LPCWSTR wzProductCodeOrPatchCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in DWORD dwCode, - __in_z LPCWSTR wzSource, - __in_opt DWORD dwIndex - ); -HRESULT DAPI WiuBeginTransaction( - __in_z LPCWSTR szName, - __in DWORD dwTransactionAttributes, - __out MSIHANDLE* phTransactionHandle, - __out HANDLE* phChangeOfOwnerEvent, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ); -HRESULT DAPI WiuEndTransaction( - __in DWORD dwTransactionState, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ); -BOOL DAPI WiuIsMsiTransactionSupported( - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/wuautil.h b/src/dutil/inc/wuautil.h deleted file mode 100644 index b239c4e6..00000000 --- a/src/dutil/inc/wuautil.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -HRESULT DAPI WuaPauseAutomaticUpdates(); - -HRESULT DAPI WuaResumeAutomaticUpdates(); - -HRESULT DAPI WuaRestartRequired( - __out BOOL* pfRestartRequired - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/dutil/inc/xmlutil.h b/src/dutil/inc/xmlutil.h deleted file mode 100644 index ba92ada9..00000000 --- a/src/dutil/inc/xmlutil.h +++ /dev/null @@ -1,167 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; - -extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; -extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; -extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}}; - -typedef enum XML_LOAD_ATTRIBUTE -{ - XML_LOAD_PRESERVE_WHITESPACE = 1, -} XML_LOAD_ATTRIBUTE; - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI XmlInitialize(); -void DAPI XmlUninitialize(); - -HRESULT DAPI XmlCreateElement( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzElementName, - __out IXMLDOMElement **ppixnElement - ); -HRESULT DAPI XmlCreateDocument( - __in_opt LPCWSTR pwzElementName, - __out IXMLDOMDocument** ppixdDocument, - __out_opt IXMLDOMElement** ppixeRootElement = NULL - ); -HRESULT DAPI XmlLoadDocument( - __in_z LPCWSTR wzDocument, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentEx( - __in_z LPCWSTR wzDocument, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentFromFile( - __in_z LPCWSTR wzPath, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentFromBuffer( - __in_bcount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentFromFileEx( - __in_z LPCWSTR wzPath, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlSelectSingleNode( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNode **ppixnChild - ); -HRESULT DAPI XmlSetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in_z LPCWSTR pwzAttributeValue - ); -HRESULT DAPI XmlCreateTextNode( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzText, - __out IXMLDOMText **ppixnTextNode - ); -HRESULT DAPI XmlGetText( - __in IXMLDOMNode* pixnNode, - __deref_out_z BSTR* pbstrText - ); -HRESULT DAPI XmlGetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __deref_out_z BSTR* pbstrAttributeValue - ); -HRESULT DAPI XmlGetAttributeEx( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __deref_out_z LPWSTR* psczAttributeValue - ); -HRESULT DAPI XmlGetYesNoAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __out BOOL* pfYes - ); -HRESULT DAPI XmlGetAttributeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD* pdwValue - ); -HRESULT DAPI XmlGetAttributeNumberBase( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in int nBase, - __out DWORD* pdwValue - ); -HRESULT DAPI XmlGetAttributeLargeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD64* pdw64Value - ); -HRESULT DAPI XmlGetNamedItem( - __in IXMLDOMNamedNodeMap *pixnmAttributes, - __in_opt LPCWSTR wzName, - __out IXMLDOMNode **ppixnNamedItem - ); -HRESULT DAPI XmlSetText( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzText - ); -HRESULT DAPI XmlSetTextNumber( - __in IXMLDOMNode *pixnNode, - __in DWORD dwValue - ); -HRESULT DAPI XmlCreateChild( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR pwzElementType, - __out IXMLDOMNode** ppixnChild - ); -HRESULT DAPI XmlRemoveAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute - ); -HRESULT DAPI XmlSelectNodes( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNodeList **ppixnChild - ); -HRESULT DAPI XmlNextAttribute( - __in IXMLDOMNamedNodeMap* pixnnm, - __out IXMLDOMNode** pixnAttribute, - __deref_opt_out_z_opt BSTR* pbstrAttribute - ); -HRESULT DAPI XmlNextElement( - __in IXMLDOMNodeList* pixnl, - __out IXMLDOMNode** pixnElement, - __deref_opt_out_z_opt BSTR* pbstrElement - ); -HRESULT DAPI XmlRemoveChildren( - __in IXMLDOMNode* pixnSource, - __in_z LPCWSTR pwzXPath - ); -HRESULT DAPI XmlSaveDocument( - __in IXMLDOMDocument* pixdDocument, - __inout LPCWSTR wzPath - ); -HRESULT DAPI XmlSaveDocumentToBuffer( - __in IXMLDOMDocument* pixdDocument, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out DWORD* pcbDest - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inetutil.cpp b/src/dutil/inetutil.cpp deleted file mode 100644 index 8dace55f..00000000 --- a/src/dutil/inetutil.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define InetExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) -#define InetExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) -#define InetExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) -#define InetExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) -#define InetExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INETUTIL, e, x, s, __VA_ARGS__) -#define InetExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INETUTIL, g, x, s, __VA_ARGS__) - - -/******************************************************************* - InternetGetSizeByHandle - returns size of file by url handle - -*******************************************************************/ -extern "C" HRESULT DAPI InternetGetSizeByHandle( - __in HINTERNET hiFile, - __out LONGLONG* pllSize - ) -{ - Assert(pllSize); - - HRESULT hr = S_OK; - DWORD dwSize = 0; - DWORD cb = 0; - - cb = sizeof(dwSize); - if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast(&dwSize), &cb, NULL)) - { - InetExitOnLastError(hr, "Failed to get size for internet file handle"); - } - - *pllSize = dwSize; -LExit: - return hr; -} - - -/******************************************************************* - InetGetCreateTimeByHandle - returns url creation time - -******************************************************************/ -extern "C" HRESULT DAPI InternetGetCreateTimeByHandle( - __in HINTERNET hiFile, - __out LPFILETIME pft - ) -{ - Assert(pft); - - HRESULT hr = S_OK; - SYSTEMTIME st = {0 }; - DWORD cb = sizeof(SYSTEMTIME); - - if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast(&st), &cb, NULL)) - { - InetExitWithLastError(hr, "failed to get create time for internet file handle"); - } - - if (!::SystemTimeToFileTime(&st, pft)) - { - InetExitWithLastError(hr, "failed to convert system time to file time"); - } - -LExit: - return hr; -} - - -/******************************************************************* - InternetQueryInfoString - query info string - -*******************************************************************/ -extern "C" HRESULT DAPI InternetQueryInfoString( - __in HINTERNET hRequest, - __in DWORD dwInfo, - __deref_out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - SIZE_T cbOriginal = 0; - DWORD cbValue = 0; - DWORD dwIndex = 0; - - // If nothing was provided start off with some arbitrary size. - if (!*psczValue) - { - hr = StrAlloc(psczValue, 64); - InetExitOnFailure(hr, "Failed to allocate memory for value."); - } - - hr = StrSize(*psczValue, &cbOriginal); - InetExitOnFailure(hr, "Failed to get size of value."); - - cbValue = (DWORD)min(DWORD_MAX, cbOriginal); - - if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) - { - DWORD er = ::GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == er) - { - cbValue += sizeof(WCHAR); // add one character for the null terminator. - - hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR)); - InetExitOnFailure(hr, "Failed to allocate value."); - - if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) - { - er = ::GetLastError(); - } - else - { - er = ERROR_SUCCESS; - } - } - - hr = HRESULT_FROM_WIN32(er); - InetExitOnRootFailure(hr, "Failed to get query information."); - } - -LExit: - return hr; -} - - -/******************************************************************* - InternetQueryInfoNumber - query info number - -*******************************************************************/ -extern "C" HRESULT DAPI InternetQueryInfoNumber( - __in HINTERNET hRequest, - __in DWORD dwInfo, - __inout LONG* plInfo - ) -{ - HRESULT hr = S_OK; - DWORD cbCode = sizeof(LONG); - DWORD dwIndex = 0; - - if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast(plInfo), &cbCode, &dwIndex)) - { - InetExitWithLastError(hr, "Failed to get query information."); - } - -LExit: - return hr; -} diff --git a/src/dutil/iniutil.cpp b/src/dutil/iniutil.cpp deleted file mode 100644 index 70b62995..00000000 --- a/src/dutil/iniutil.cpp +++ /dev/null @@ -1,768 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) -#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) -#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) -#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) -#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__) -#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__) - -const LPCWSTR wzSectionSeparator = L"\\"; - -struct INI_STRUCT -{ - LPWSTR sczPath; // the path to the INI file to be parsed - - LPWSTR sczOpenTagPrefix; // For regular ini, this would be '[' - LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']' - - LPWSTR sczValuePrefix; // for regular ini, this would be NULL - LPWSTR sczValueSeparator; // for regular ini, this would be '=' - - LPWSTR *rgsczValueSeparatorExceptions; - DWORD cValueSeparatorExceptions; - - LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';' - - INI_VALUE *rgivValues; - DWORD cValues; - - LPWSTR *rgsczLines; - DWORD cLines; - - FILE_ENCODING feEncoding; - BOOL fModified; -}; - -const int INI_HANDLE_BYTES = sizeof(INI_STRUCT); - -static HRESULT GetSectionPrefixFromName( - __in_z LPCWSTR wzName, - __deref_inout_z LPWSTR* psczOutput - ); -static void UninitializeIniValue( - INI_VALUE *pivValue - ); - -extern "C" HRESULT DAPI IniInitialize( - __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle - ) -{ - HRESULT hr = S_OK; - - // Allocate the handle - *piHandle = static_cast(MemAlloc(sizeof(INI_STRUCT), TRUE)); - IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); - -LExit: - return hr; -} - -extern "C" void DAPI IniUninitialize( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle - ) -{ - INI_STRUCT *pi = static_cast(piHandle); - - ReleaseStr(pi->sczPath); - ReleaseStr(pi->sczOpenTagPrefix); - ReleaseStr(pi->sczOpenTagPostfix); - ReleaseStr(pi->sczValuePrefix); - ReleaseStr(pi->sczValueSeparator); - - for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i) - { - ReleaseStr(pi->rgsczValueSeparatorExceptions + i); - } - - ReleaseStr(pi->sczCommentLinePrefix); - - for (DWORD i = 0; i < pi->cValues; ++i) - { - UninitializeIniValue(pi->rgivValues + i); - } - ReleaseMem(pi->rgivValues); - - ReleaseStrArray(pi->rgsczLines, pi->cLines); - - ReleaseMem(pi); -} - -extern "C" HRESULT DAPI IniSetOpenTag( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzOpenTagPrefix, - __in_z_opt LPCWSTR wzOpenTagPostfix - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - if (wzOpenTagPrefix) - { - hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0); - IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); - } - else - { - ReleaseNullStr(pi->sczOpenTagPrefix); - } - - if (wzOpenTagPostfix) - { - hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0); - IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); - } - else - { - ReleaseNullStr(pi->sczOpenTagPrefix); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetValueStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzValuePrefix, - __in_z_opt LPCWSTR wzValueSeparator - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - if (wzValuePrefix) - { - hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0); - IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); - } - else - { - ReleaseNullStr(pi->sczValuePrefix); - } - - if (wzValueSeparator) - { - hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0); - IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); - } - else - { - ReleaseNullStr(pi->sczValueSeparator); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetValueSeparatorException( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z LPCWSTR wzValueNamePrefix - ) -{ - HRESULT hr = S_OK; - DWORD dwInsertedIndex = 0; - - INI_STRUCT *pi = static_cast(piHandle); - - hr = MemEnsureArraySize(reinterpret_cast(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5); - IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); - dwInsertedIndex = pi->cValueSeparatorExceptions; - ++pi->cValueSeparatorExceptions; - - hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0); - IniExitOnFailure(hr, "Failed to copy value separator exception"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetCommentStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzLinePrefix - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - if (wzLinePrefix) - { - hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0); - IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); - } - else - { - ReleaseNullStr(pi->sczCommentLinePrefix); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniParse( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzPath, - __out_opt FILE_ENCODING *pfeEncodingFound - ) -{ - HRESULT hr = S_OK; - DWORD dwValuePrefixLength = 0; - DWORD dwValueSeparatorExceptionLength = 0; - LPWSTR sczContents = NULL; - LPWSTR sczCurrentSection = NULL; - LPWSTR sczName = NULL; - LPWSTR sczNameTrimmed = NULL; - LPWSTR sczValue = NULL; - LPWSTR sczValueTrimmed = NULL; - LPWSTR wzOpenTagPrefix = NULL; - LPWSTR wzOpenTagPostfix = NULL; - LPWSTR wzValuePrefix = NULL; - LPWSTR wzValueNameStart = NULL; - LPWSTR wzValueSeparator = NULL; - LPWSTR wzCommentLinePrefix = NULL; - LPWSTR wzValueBegin = NULL; - LPCWSTR wzTemp = NULL; - - INI_STRUCT *pi = static_cast(piHandle); - - BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix); - BOOL fValuePrefix = (NULL != pi->sczValuePrefix); - - hr = StrAllocString(&pi->sczPath, wzPath, 0); - IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); - - hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding); - IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); - - if (pfeEncodingFound) - { - *pfeEncodingFound = pi->feEncoding; - } - - if (!sczContents || !*sczContents) - { - // Empty string, nothing to parse - ExitFunction1(hr = S_OK); - } - - dwValuePrefixLength = lstrlenW(pi->sczValuePrefix); - hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast(&pi->cLines), sczContents, L"\n"); - IniExitOnFailure(hr, "Failed to split INI file into lines"); - - for (DWORD i = 0; i < pi->cLines; ++i) - { - if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i]) - { - continue; - } - - if (pi->sczCommentLinePrefix) - { - wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix); - - if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1) - { - continue; - } - } - - if (pi->sczOpenTagPrefix) - { - wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix); - if (wzOpenTagPrefix) - { - // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix - // This is important, for example, to support values with names like "Array[0]=blah" in INI format - for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp) - { - if (*wzTemp != L' ' && *wzTemp != L'\t') - { - wzOpenTagPrefix = NULL; - break; - } - } - } - } - - if (pi->sczOpenTagPostfix) - { - wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix); - } - - if (pi->sczValuePrefix) - { - wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix); - if (wzValuePrefix != NULL) - { - wzValueNameStart = wzValuePrefix + dwValuePrefixLength; - } - } - else - { - wzValueNameStart = pi->rgsczLines[i]; - } - - if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0') - { - dwValueSeparatorExceptionLength = 0; - for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j) - { - if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j])) - { - dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]); - break; - } - } - - wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator); - } - - // Don't keep the endline - if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r') - { - pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0'; - } - - if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix)) - { - // There is an section starting here, let's keep track of it and move on - hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix))); - IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); - - // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output - ReleaseNullStr(pi->rgsczLines[i]); - } - else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix) - && (!fValuePrefix || wzValuePrefix)) - { - if (fValuePrefix) - { - wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix); - } - else - { - wzValueBegin = pi->rgsczLines[i]; - } - - hr = MemEnsureArraySize(reinterpret_cast(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100); - IniExitOnFailure(hr, "Failed to increase array size for value array"); - - if (sczCurrentSection) - { - hr = StrAllocString(&sczName, sczCurrentSection, 0); - IniExitOnFailure(hr, "Failed to copy current section name"); - - hr = StrAllocConcat(&sczName, wzSectionSeparator, 0); - IniExitOnFailure(hr, "Failed to copy current section name"); - } - - hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin); - IniExitOnFailure(hr, "Failed to copy name"); - - hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0); - IniExitOnFailure(hr, "Failed to copy value"); - - hr = StrTrimWhitespace(&sczNameTrimmed, sczName); - IniExitOnFailure(hr, "Failed to trim whitespace from name"); - - hr = StrTrimWhitespace(&sczValueTrimmed, sczValue); - IniExitOnFailure(hr, "Failed to trim whitespace from value"); - - pi->rgivValues[pi->cValues].wzName = const_cast(sczNameTrimmed); - sczNameTrimmed = NULL; - pi->rgivValues[pi->cValues].wzValue = const_cast(sczValueTrimmed); - sczValueTrimmed = NULL; - pi->rgivValues[pi->cValues].dwLineNumber = i + 1; - - ++pi->cValues; - - // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output - ReleaseNullStr(pi->rgsczLines[i]); - } - else - { - // Must be a comment, so ignore it and keep it in the list to output - } - - ReleaseNullStr(sczName); - } - -LExit: - ReleaseStr(sczCurrentSection); - ReleaseStr(sczContents); - ReleaseStr(sczName); - ReleaseStr(sczNameTrimmed); - ReleaseStr(sczValue); - ReleaseStr(sczValueTrimmed); - - return hr; -} - -extern "C" HRESULT DAPI IniGetValueList( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, - __out DWORD *pcValues - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - *prgivValues = pi->rgivValues; - *pcValues = pi->cValues; - - return hr; -} - -extern "C" HRESULT DAPI IniGetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __deref_out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - INI_VALUE *pValue = NULL; - - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) - { - pValue = pi->rgivValues + i; - break; - } - } - - if (NULL == pValue) - { - hr = E_NOTFOUND; - IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); - } - - if (NULL == pValue->wzValue) - { - ExitFunction1(hr = E_NOTFOUND); - } - - hr = StrAllocString(psczValue, pValue->wzValue, 0); - IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __in_z_opt LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczSectionPrefix = NULL; // includes section name and backslash - LPWSTR sczName = NULL; - LPWSTR sczValue = NULL; - DWORD dwInsertIndex = DWORD_MAX; - - INI_STRUCT *pi = static_cast(piHandle); - INI_VALUE *pValue = NULL; - - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) - { - pValue = pi->rgivValues + i; - break; - } - } - - // We're killing the value - if (NULL == wzValue) - { - if (pValue && pValue->wzValue) - { - pi->fModified = TRUE; - sczValue = const_cast(pValue->wzValue); - pValue->wzValue = NULL; - ReleaseNullStr(sczValue); - } - - ExitFunction(); - } - else - { - if (pValue) - { - if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1)) - { - pi->fModified = TRUE; - hr = StrAllocString(const_cast(&pValue->wzValue), wzValue, 0); - IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); - } - - ExitFunction1(hr = S_OK); - } - else - { - if (wzValueName) - { - hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix); - IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); - } - - // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in) - if (sczSectionPrefix) - { - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix))) - { - dwInsertIndex = i; - } - else if (DWORD_MAX != dwInsertIndex) - { - break; - } - } - } - else - { - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator)) - { - dwInsertIndex = i; - } - else if (DWORD_MAX != dwInsertIndex) - { - break; - } - } - } - - // Otherwise, just add it to the end - if (DWORD_MAX == dwInsertIndex) - { - dwInsertIndex = pi->cValues; - } - - pi->fModified = TRUE; - hr = MemInsertIntoArray(reinterpret_cast(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100); - IniExitOnFailure(hr, "Failed to insert value into array"); - - hr = StrAllocString(&sczName, wzValueName, 0); - IniExitOnFailure(hr, "Failed to copy name"); - - hr = StrAllocString(&sczValue, wzValue, 0); - IniExitOnFailure(hr, "Failed to copy value"); - - pi->rgivValues[dwInsertIndex].wzName = const_cast(sczName); - sczName = NULL; - pi->rgivValues[dwInsertIndex].wzValue = const_cast(sczValue); - sczValue = NULL; - - ++pi->cValues; - } - } - -LExit: - ReleaseStr(sczName); - ReleaseStr(sczValue); - - return hr; -} - -extern "C" HRESULT DAPI IniWriteFile( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzPath, - __in FILE_ENCODING feOverrideEncoding - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCurrentSectionPrefix = NULL; - LPWSTR sczNewSectionPrefix = NULL; - LPWSTR sczContents = NULL; - LPCWSTR wzName = NULL; - DWORD dwLineArrayIndex = 1; - FILE_ENCODING feEncoding; - - INI_STRUCT *pi = static_cast(piHandle); - - if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding) - { - feEncoding = pi->feEncoding; - } - else - { - feEncoding = feOverrideEncoding; - } - - if (FILE_ENCODING_UNSPECIFIED == feEncoding) - { - feEncoding = FILE_ENCODING_UTF16_WITH_BOM; - } - - if (!pi->fModified) - { - ExitFunction1(hr = S_OK); - } - if (NULL == wzPath && NULL == pi->sczPath) - { - ExitFunction1(hr = E_NOTFOUND); - } - - BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix); - - hr = StrAllocString(&sczContents, L"", 0); - IniExitOnFailure(hr, "Failed to begin contents string as empty string"); - - // Insert any beginning lines we didn't understand like comments - if (0 < pi->cLines) - { - while (pi->rgsczLines[dwLineArrayIndex]) - { - hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0); - IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - - ++dwLineArrayIndex; - } - } - - for (DWORD i = 0; i < pi->cValues; ++i) - { - // Skip if this value was killed off - if (NULL == pi->rgivValues[i].wzValue) - { - continue; - } - - // Now generate any lines for the current value like value line and maybe also a new section line before it - - // First see if we need to write a section line - hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix); - IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); - - // If the new section prefix is different, write a section out for it - if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1))) - { - hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0); - IniExitOnFailure(hr, "Failed to concat open tag prefix to string"); - - // Exclude section separator (i.e. backslash) from new section prefix - hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator)); - IniExitOnFailure(hr, "Failed to concat section name to string"); - - hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0); - IniExitOnFailure(hr, "Failed to concat open tag postfix to string"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - - ReleaseNullStr(sczCurrentSectionPrefix); - sczCurrentSectionPrefix = sczNewSectionPrefix; - sczNewSectionPrefix = NULL; - } - - // Inserting lines we read before the current value if appropriate - while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex) - { - // Skip any lines were purposely forgot - if (NULL == pi->rgsczLines[dwLineArrayIndex]) - { - ++dwLineArrayIndex; - continue; - } - - hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0); - IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - } - - wzName = pi->rgivValues[i].wzName; - if (fSections) - { - wzName += lstrlenW(sczCurrentSectionPrefix); - } - - // OK, now just write the name/value pair, if it isn't deleted - if (pi->sczValuePrefix) - { - hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0); - IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); - } - - hr = StrAllocConcat(&sczContents, wzName, 0); - IniExitOnFailure(hr, "Failed to concat value name to ini output buffer"); - - hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0); - IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); - - hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0); - IniExitOnFailure(hr, "Failed to concat value to ini output buffer"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - } - - // If no path was specified, use the path to the file we parsed - if (NULL == wzPath) - { - wzPath = pi->sczPath; - } - - hr = FileFromString(wzPath, 0, sczContents, feEncoding); - IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); - -LExit: - ReleaseStr(sczContents); - ReleaseStr(sczCurrentSectionPrefix); - ReleaseStr(sczNewSectionPrefix); - - return hr; -} - -static void UninitializeIniValue( - INI_VALUE *pivValue - ) -{ - ReleaseStr(const_cast(pivValue->wzName)); - ReleaseStr(const_cast(pivValue->wzValue)); -} - -static HRESULT GetSectionPrefixFromName( - __in_z LPCWSTR wzName, - __deref_inout_z LPWSTR* psczOutput - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzSectionDelimiter = NULL; - - ReleaseNullStr(*psczOutput); - - wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator); - if (wzSectionDelimiter && wzSectionDelimiter != wzName) - { - hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1); - IniExitOnFailure(hr, "Failed to copy section prefix"); - } - -LExit: - return hr; -} diff --git a/src/dutil/jsonutil.cpp b/src/dutil/jsonutil.cpp deleted file mode 100644 index 3450ba59..00000000 --- a/src/dutil/jsonutil.cpp +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define JsonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) -#define JsonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) -#define JsonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) -#define JsonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) -#define JsonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_JSONUTIL, e, x, s, __VA_ARGS__) -#define JsonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_JSONUTIL, g, x, s, __VA_ARGS__) - -const DWORD JSON_STACK_INCREMENT = 5; - -// Prototypes -static HRESULT DoStart( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenStart, - __in_z LPCWSTR wzStartString - ); -static HRESULT DoEnd( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenEnd, - __in_z LPCWSTR wzEndString - ); -static HRESULT DoKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ); -static HRESULT DoValue( - __in JSON_WRITER* pWriter, - __in_z_opt LPCWSTR wzValue - ); -static HRESULT EnsureTokenStack( - __in JSON_WRITER* pWriter - ); -static HRESULT SerializeJsonString( - __out_z LPWSTR* psczJsonString, - __in_z LPCWSTR wzString - ); - - - -DAPI_(HRESULT) JsonInitializeReader( - __in_z LPCWSTR wzJson, - __in JSON_READER* pReader - ) -{ - HRESULT hr = S_OK; - - memset(pReader, 0, sizeof(JSON_READER)); - ::InitializeCriticalSection(&pReader->cs); - - hr = StrAllocString(&pReader->sczJson, wzJson, 0); - JsonExitOnFailure(hr, "Failed to allocate json string."); - - pReader->pwz = pReader->sczJson; - -LExit: - return hr; -} - - -DAPI_(void) JsonUninitializeReader( - __in JSON_READER* pReader - ) -{ - ReleaseStr(pReader->sczJson); - - ::DeleteCriticalSection(&pReader->cs); - memset(pReader, 0, sizeof(JSON_READER)); -} - - -static HRESULT NextToken( - __in JSON_READER* pReader, - __out JSON_TOKEN* pToken - ) -{ - HRESULT hr = S_OK; - - // Skip whitespace. - while (L' ' == *pReader->pwz || - L'\t' == *pReader->pwz || - L'\r' == *pReader->pwz || - L'\n' == *pReader->pwz || - L',' == *pReader->pwz) - { - ++pReader->pwz; - } - - switch (*pReader->pwz) - { - case L'{': - *pToken = JSON_TOKEN_OBJECT_START; - break; - - case L':': // begin object value - *pToken = JSON_TOKEN_OBJECT_VALUE; - break; - - case L'}': // end object - *pToken = JSON_TOKEN_OBJECT_END; - break; - - case L'[': // begin array - *pToken = JSON_TOKEN_ARRAY_START; - break; - - case L']': // end array - *pToken = JSON_TOKEN_ARRAY_END; - break; - - case L'"': // string - *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY; - break; - - case L't': // true - case L'f': // false - case L'n': // null - case L'-': // number - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - *pToken = JSON_TOKEN_VALUE; - break; - - case L'\0': - hr = E_NOMOREITEMS; - break; - - default: - hr = E_INVALIDSTATE; - } - - return hr; -} - - -DAPI_(HRESULT) JsonReadNext( - __in JSON_READER* pReader, - __out JSON_TOKEN* pToken, - __out JSON_VALUE* pValue - ) -{ - HRESULT hr = S_OK; - //WCHAR wz; - //JSON_TOKEN token = JSON_TOKEN_NONE; - - ::EnterCriticalSection(&pReader->cs); - - hr = NextToken(pReader, pToken); - if (E_NOMOREITEMS == hr) - { - ExitFunction(); - } - JsonExitOnFailure(hr, "Failed to get next token."); - - if (JSON_TOKEN_VALUE == *pToken) - { - hr = JsonReadValue(pReader, pValue); - } - else - { - ++pReader->pwz; - } - -LExit: - ::LeaveCriticalSection(&pReader->cs); - return hr; -} - - -DAPI_(HRESULT) JsonReadValue( - __in JSON_READER* /*pReader*/, - __in JSON_VALUE* /*pValue*/ - ) -{ - HRESULT hr = S_OK; - -//LExit: - return hr; -} - - -DAPI_(HRESULT) JsonInitializeWriter( - __in JSON_WRITER* pWriter - ) -{ - memset(pWriter, 0, sizeof(JSON_WRITER)); - ::InitializeCriticalSection(&pWriter->cs); - - return S_OK; -} - - -DAPI_(void) JsonUninitializeWriter( - __in JSON_WRITER* pWriter - ) -{ - ReleaseMem(pWriter->rgTokenStack); - ReleaseStr(pWriter->sczJson); - - ::DeleteCriticalSection(&pWriter->cs); - memset(pWriter, 0, sizeof(JSON_WRITER)); -} - - -DAPI_(HRESULT) JsonWriteBool( - __in JSON_WRITER* pWriter, - __in BOOL fValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0); - JsonExitOnFailure(hr, "Failed to convert boolean to string."); - - hr = DoValue(pWriter, sczValue); - JsonExitOnFailure(hr, "Failed to add boolean to JSON."); - -LExit: - ReleaseStr(sczValue); - return hr; -} - - -DAPI_(HRESULT) JsonWriteNumber( - __in JSON_WRITER* pWriter, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - hr = StrAllocFormatted(&sczValue, L"%u", dwValue); - JsonExitOnFailure(hr, "Failed to convert number to string."); - - hr = DoValue(pWriter, sczValue); - JsonExitOnFailure(hr, "Failed to add number to JSON."); - -LExit: - ReleaseStr(sczValue); - return hr; -} - - -DAPI_(HRESULT) JsonWriteString( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczJsonString = NULL; - - hr = SerializeJsonString(&sczJsonString, wzValue); - JsonExitOnFailure(hr, "Failed to allocate string JSON."); - - hr = DoValue(pWriter, sczJsonString); - JsonExitOnFailure(hr, "Failed to add string to JSON."); - -LExit: - ReleaseStr(sczJsonString); - return hr; -} - - -DAPI_(HRESULT) JsonWriteArrayStart( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"["); - JsonExitOnFailure(hr, "Failed to start JSON array."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) JsonWriteArrayEnd( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]"); - JsonExitOnFailure(hr, "Failed to end JSON array."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) JsonWriteObjectStart( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{"); - JsonExitOnFailure(hr, "Failed to start JSON object."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) JsonWriteObjectKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ) -{ - HRESULT hr = S_OK; - LPWSTR sczObjectKey = NULL; - - hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey); - JsonExitOnFailure(hr, "Failed to allocate JSON object key."); - - hr = DoKey(pWriter, sczObjectKey); - JsonExitOnFailure(hr, "Failed to add object key to JSON."); - -LExit: - ReleaseStr(sczObjectKey); - return hr; -} - - -DAPI_(HRESULT) JsonWriteObjectEnd( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}"); - JsonExitOnFailure(hr, "Failed to end JSON object."); - -LExit: - return hr; -} - - -static HRESULT DoStart( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenStart, - __in_z LPCWSTR wzStartString - ) -{ - Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart); - - HRESULT hr = S_OK; - JSON_TOKEN token = JSON_TOKEN_NONE; - BOOL fNeedComma = FALSE; - BOOL fPushToken = TRUE; - - ::EnterCriticalSection(&pWriter->cs); - - hr = EnsureTokenStack(pWriter); - JsonExitOnFailure(hr, "Failed to ensure token stack for start."); - - token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - switch (token) - { - case JSON_TOKEN_NONE: - token = tokenStart; - fPushToken = FALSE; - break; - - case JSON_TOKEN_ARRAY_START: // array start changes to array value. - token = JSON_TOKEN_ARRAY_VALUE; - break; - - case JSON_TOKEN_ARRAY_VALUE: - case JSON_TOKEN_ARRAY_END: - case JSON_TOKEN_OBJECT_END: - fNeedComma = TRUE; - break; - - default: // everything else is not allowed. - hr = E_UNEXPECTED; - break; - } - JsonExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); - - if (fNeedComma) - { - hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - JsonExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); - } - - hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); - JsonExitOnFailure(hr, "Failed to start JSON array or object."); - - pWriter->rgTokenStack[pWriter->cTokens - 1] = token; - if (fPushToken) - { - pWriter->rgTokenStack[pWriter->cTokens] = tokenStart; - ++pWriter->cTokens; - } - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT DoEnd( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenEnd, - __in_z LPCWSTR wzEndString - ) -{ - HRESULT hr = S_OK; - - ::EnterCriticalSection(&pWriter->cs); - - if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) - { - hr = E_UNEXPECTED; - JsonExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); - } - else - { - JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) || - (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) - { - hr = E_UNEXPECTED; - JsonExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); - } - } - - hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); - JsonExitOnFailure(hr, "Failed to end JSON array or object."); - - --pWriter->cTokens; - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT DoKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ) -{ - HRESULT hr = S_OK; - JSON_TOKEN token = JSON_TOKEN_NONE; - BOOL fNeedComma = FALSE; - - ::EnterCriticalSection(&pWriter->cs); - - hr = EnsureTokenStack(pWriter); - JsonExitOnFailure(hr, "Failed to ensure token stack for key."); - - token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - switch (token) - { - case JSON_TOKEN_OBJECT_START: - token = JSON_TOKEN_OBJECT_KEY; - break; - - case JSON_TOKEN_OBJECT_VALUE: - token = JSON_TOKEN_OBJECT_KEY; - fNeedComma = TRUE; - break; - - default: // everything else is not allowed. - hr = E_UNEXPECTED; - break; - } - JsonExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); - - if (fNeedComma) - { - hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - JsonExitOnFailure(hr, "Failed to add comma for key to JSON."); - } - - hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); - JsonExitOnFailure(hr, "Failed to add key to JSON."); - - pWriter->rgTokenStack[pWriter->cTokens - 1] = token; - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT DoValue( - __in JSON_WRITER* pWriter, - __in_z_opt LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - JSON_TOKEN token = JSON_TOKEN_NONE; - BOOL fNeedComma = FALSE; - - ::EnterCriticalSection(&pWriter->cs); - - hr = EnsureTokenStack(pWriter); - JsonExitOnFailure(hr, "Failed to ensure token stack for value."); - - token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - switch (token) - { - case JSON_TOKEN_ARRAY_START: - token = JSON_TOKEN_ARRAY_VALUE; - break; - - case JSON_TOKEN_ARRAY_VALUE: - fNeedComma = TRUE; - break; - - case JSON_TOKEN_OBJECT_KEY: - token = JSON_TOKEN_OBJECT_VALUE; - break; - - case JSON_TOKEN_NONE: - token = JSON_TOKEN_VALUE; - break; - - default: // everything else is not allowed. - hr = E_UNEXPECTED; - break; - } - JsonExitOnRootFailure(hr, "Cannot add value to JSON serializer now."); - - if (fNeedComma) - { - hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - JsonExitOnFailure(hr, "Failed to add comma for value to JSON."); - } - - if (wzValue) - { - hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0); - JsonExitOnFailure(hr, "Failed to add value to JSON."); - } - else - { - hr = StrAllocConcat(&pWriter->sczJson, L"null", 0); - JsonExitOnFailure(hr, "Failed to add null value to JSON."); - } - - pWriter->rgTokenStack[pWriter->cTokens - 1] = token; - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT EnsureTokenStack( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0; - - hr = MemEnsureArraySize(reinterpret_cast(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT); - JsonExitOnFailure(hr, "Failed to allocate JSON token stack."); - - if (0 == pWriter->cTokens) - { - pWriter->rgTokenStack[0] = JSON_TOKEN_NONE; - ++pWriter->cTokens; - } - -LExit: - return hr; -} - - -static HRESULT SerializeJsonString( - __out_z LPWSTR* psczJsonString, - __in_z LPCWSTR wzString - ) -{ - HRESULT hr = S_OK; - DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0) - - for (LPCWSTR pch = wzString; *pch; ++pch) - { - // If it is a special JSON character, add space for the escape backslash. - if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch) - { - ++cchRequired; - } - - ++cchRequired; - } - - hr = StrAlloc(psczJsonString, cchRequired); - JsonExitOnFailure(hr, "Failed to allocate space for JSON string."); - - LPWSTR pchTarget = *psczJsonString; - - *pchTarget = L'\"'; - ++pchTarget; - - for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget) - { - // If it is a special JSON character, handle it or just add the character as is. - switch (*pch) - { - case L'"': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'"'; - break; - - case L'\\': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'\\'; - break; - - case L'/': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'/'; - break; - - case L'\b': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'b'; - break; - - case L'\f': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'f'; - break; - - case L'\n': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'n'; - break; - - case L'\r': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'r'; - break; - - case L'\t': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L't'; - break; - - default: - *pchTarget = *pch; - break; - } - - } - - *pchTarget = L'\"'; - ++pchTarget; - *pchTarget = L'\0'; - -LExit: - return hr; -} diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp deleted file mode 100644 index c4567c03..00000000 --- a/src/dutil/locutil.cpp +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define LocExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) -#define LocExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) -#define LocExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) -#define LocExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) -#define LocExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOCUTIL, e, x, s, __VA_ARGS__) -#define LocExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOCUTIL, g, x, s, __VA_ARGS__) - -// prototypes -static HRESULT ParseWxl( - __in IXMLDOMDocument* pixd, - __out WIX_LOCALIZATION** ppWixLoc - ); -static HRESULT ParseWxlStrings( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ); -static HRESULT ParseWxlControls( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ); -static HRESULT ParseWxlString( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ); -static HRESULT ParseWxlControl( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ); - -// from Winnls.h -#ifndef MUI_LANGUAGE_ID -#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention -#endif -#ifndef MUI_MERGE_USER_FALLBACK -#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages -#endif -#ifndef MUI_MERGE_SYSTEM_FALLBACK -#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages -#endif -typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( - __in DWORD dwFlags, - __out PULONG pulNumLanguages, - __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, - __inout PULONG pcchLanguagesBuffer -); - -extern "C" HRESULT DAPI LocProbeForFile( - __in_z LPCWSTR wzBasePath, - __in_z LPCWSTR wzLocFileName, - __in_z_opt LPCWSTR wzLanguage, - __inout LPWSTR* psczPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczProbePath = NULL; - LANGID langid = 0; - LPWSTR sczLangIdFile = NULL; - LPWSTR sczLangsBuff = NULL; - GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = - reinterpret_cast( - GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); - - // If a language was specified, look for a loc file in that as a directory. - if (wzLanguage && *wzLanguage) - { - hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat base path to language."); - - hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat loc file name to probe path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - - if (pvfnGetThreadPreferredUILanguages) - { - ULONG nLangs; - ULONG cchLangs = 0; - DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; - if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) - { - LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); - } - - hr = StrAlloc(&sczLangsBuff, cchLangs); - LocExitOnFailure(hr, "Failed to allocate buffer for languages"); - - nLangs = 0; - if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) - { - LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); - } - - LPWSTR szLangs = sczLangsBuff; - for (ULONG i = 0; i < nLangs; ++i, szLangs += 5) - { - // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value. - hr = StrHexDecode(szLangs, reinterpret_cast(&langid), sizeof(langid)); - LocExitOnFailure(hr, "Failed to parse langId."); - - langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid)); - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user preferred langid."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user preferred langid file name to base path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - } - - langid = ::GetUserDefaultUILanguage(); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user langid."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user langid file name to base path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - - if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) - { - langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - - langid = ::GetSystemDefaultUILanguage(); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format system langid."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat system langid file name to base path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - - if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) - { - langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - - // Finally, look for the loc file in the base path. - hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat loc file name to base path."); - - if (!FileExistsEx(sczProbePath, NULL)) - { - hr = E_FILENOTFOUND; - } - -LExit: - if (SUCCEEDED(hr)) - { - hr = StrAllocString(psczPath, sczProbePath, 0); - } - - ReleaseStr(sczLangIdFile); - ReleaseStr(sczProbePath); - ReleaseStr(sczLangsBuff); - - return hr; -} - -extern "C" HRESULT DAPI LocLoadFromFile( - __in_z LPCWSTR wzWxlFile, - __out WIX_LOCALIZATION** ppWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixd = NULL; - - hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd); - LocExitOnFailure(hr, "Failed to load WXL file as XML document."); - - hr = ParseWxl(pixd, ppWixLoc); - LocExitOnFailure(hr, "Failed to parse WXL."); - -LExit: - ReleaseObject(pixd); - - return hr; -} - -extern "C" HRESULT DAPI LocLoadFromResource( - __in HMODULE hModule, - __in_z LPCSTR szResource, - __out WIX_LOCALIZATION** ppWixLoc - ) -{ - HRESULT hr = S_OK; - LPVOID pvResource = NULL; - DWORD cbResource = 0; - LPWSTR sczXml = NULL; - IXMLDOMDocument* pixd = NULL; - - hr = ResReadData(hModule, szResource, &pvResource, &cbResource); - LocExitOnFailure(hr, "Failed to read theme from resource."); - - hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); - LocExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); - - hr = XmlLoadDocument(sczXml, &pixd); - LocExitOnFailure(hr, "Failed to load theme resource as XML document."); - - hr = ParseWxl(pixd, ppWixLoc); - LocExitOnFailure(hr, "Failed to parse WXL."); - -LExit: - ReleaseObject(pixd); - ReleaseStr(sczXml); - - return hr; -} - -extern "C" void DAPI LocFree( - __in_opt WIX_LOCALIZATION* pWixLoc - ) -{ - if (pWixLoc) - { - for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) - { - ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); - ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); - } - - for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) - { - ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); - ReleaseStr(pWixLoc->rgLocControls[idx].wzText); - } - - ReleaseMem(pWixLoc->rgLocStrings); - ReleaseMem(pWixLoc->rgLocControls); - ReleaseMem(pWixLoc); - } -} - -extern "C" HRESULT DAPI LocLocalizeString( - __in const WIX_LOCALIZATION* pWixLoc, - __inout LPWSTR* ppsczInput - ) -{ - Assert(ppsczInput && pWixLoc); - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) - { - hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText); - LocExitOnFailure(hr, "Localizing string failed."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI LocGetControl( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_CONTROL** ppLocControl - ) -{ - HRESULT hr = S_OK; - LOC_CONTROL* pLocControl = NULL; - - for (DWORD i = 0; i < pWixLoc->cLocControls; ++i) - { - pLocControl = &pWixLoc->rgLocControls[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1)) - { - *ppLocControl = pLocControl; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI LocGetString( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_STRING** ppLocString - ) -{ - HRESULT hr = E_NOTFOUND; - LOC_STRING* pLocString = NULL; - - for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) - { - pLocString = pWixLoc->rgLocStrings + i; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1)) - { - *ppLocString = pLocString; - hr = S_OK; - break; - } - } - - return hr; -} - -extern "C" HRESULT DAPI LocAddString( - __in WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __in_z LPCWSTR wzLocString, - __in BOOL bOverridable - ) -{ - HRESULT hr = S_OK; - - ++pWixLoc->cLocStrings; - pWixLoc->rgLocStrings = static_cast(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); - LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings."); - - LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1); - - hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId); - LocExitOnFailure(hr, "Failed to set localization string Id."); - - hr = StrAllocString(&pLocString->wzText, wzLocString, 0); - LocExitOnFailure(hr, "Failed to set localization string Text."); - - pLocString->bOverridable = bOverridable; - -LExit: - return hr; -} - -// helper functions - -static HRESULT ParseWxl( - __in IXMLDOMDocument* pixd, - __out WIX_LOCALIZATION** ppWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMElement *pWxlElement = NULL; - WIX_LOCALIZATION* pWixLoc = NULL; - - pWixLoc = static_cast(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE)); - LocExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file."); - - // read the WixLocalization tag - hr = pixd->get_documentElement(&pWxlElement); - LocExitOnFailure(hr, "Failed to get localization element."); - - // get the Language attribute if present - pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET; - hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId); - if (S_FALSE == hr) - { - hr = S_OK; - } - LocExitOnFailure(hr, "Failed to get Language value."); - - // store the strings and controls in a node list - hr = ParseWxlStrings(pWxlElement, pWixLoc); - LocExitOnFailure(hr, "Parsing localization strings failed."); - - hr = ParseWxlControls(pWxlElement, pWixLoc); - LocExitOnFailure(hr, "Parsing localization controls failed."); - - *ppWixLoc = pWixLoc; - pWixLoc = NULL; - -LExit: - ReleaseObject(pWxlElement); - ReleaseMem(pWixLoc); - - return hr; -} - - -static HRESULT ParseWxlStrings( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - IXMLDOMNodeList* pixnl = NULL; - DWORD dwIdx = 0; - - hr = XmlSelectNodes(pElement, L"String", &pixnl); - LocExitOnLastError(hr, "Failed to get String child nodes of Wxl File."); - - hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocStrings)); - LocExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File."); - - if (0 < pWixLoc->cLocStrings) - { - pWixLoc->rgLocStrings = static_cast(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); - LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) - { - hr = ParseWxlString(pixn, dwIdx, pWixLoc); - LocExitOnFailure(hr, "Failed to parse localization string."); - - ++dwIdx; - ReleaseNullObject(pixn); - } - - hr = S_OK; - LocExitOnFailure(hr, "Failed to enumerate all localization strings."); - } - -LExit: - if (FAILED(hr) && pWixLoc->rgLocStrings) - { - for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) - { - ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); - ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); - } - - ReleaseMem(pWixLoc->rgLocStrings); - } - - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - -static HRESULT ParseWxlControls( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - IXMLDOMNodeList* pixnl = NULL; - DWORD dwIdx = 0; - - hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl); - LocExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); - - hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocControls)); - LocExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); - - if (0 < pWixLoc->cLocControls) - { - pWixLoc->rgLocControls = static_cast(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE)); - LocExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) - { - hr = ParseWxlControl(pixn, dwIdx, pWixLoc); - LocExitOnFailure(hr, "Failed to parse localized control."); - - ++dwIdx; - ReleaseNullObject(pixn); - } - - hr = S_OK; - LocExitOnFailure(hr, "Failed to enumerate all localized controls."); - } - -LExit: - if (FAILED(hr) && pWixLoc->rgLocControls) - { - for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) - { - ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); - ReleaseStr(pWixLoc->rgLocControls[idx].wzText); - } - - ReleaseMem(pWixLoc->rgLocControls); - } - - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - -static HRESULT ParseWxlString( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - LOC_STRING* pLocString = NULL; - BSTR bstrText = NULL; - - pLocString = pWixLoc->rgLocStrings + dwIdx; - - // Id - hr = XmlGetAttribute(pixn, L"Id", &bstrText); - LocExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file."); - - hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText); - LocExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file."); - - ReleaseNullBSTR(bstrText); - - // Overrideable - hr = XmlGetAttribute(pixn, L"Overridable", &bstrText); - LocExitOnFailure(hr, "Failed to get Xml attribute Overridable."); - - if (S_OK == hr) - { - pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1); - } - - ReleaseNullBSTR(bstrText); - - // Text - hr = XmlGetText(pixn, &bstrText); - LocExitOnFailure(hr, "Failed to get Xml text in Wxl file."); - - hr = StrAllocString(&pLocString->wzText, bstrText, 0); - LocExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file."); - -LExit: - ReleaseBSTR(bstrText); - - return hr; -} - -static HRESULT ParseWxlControl( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - LOC_CONTROL* pLocControl = NULL; - BSTR bstrText = NULL; - - pLocControl = pWixLoc->rgLocControls + dwIdx; - - // Id - hr = XmlGetAttribute(pixn, L"Control", &bstrText); - LocExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file."); - - hr = StrAllocString(&pLocControl->wzControl, bstrText, 0); - LocExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file."); - - ReleaseNullBSTR(bstrText); - - // X - pLocControl->nX = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pLocControl->nX)); - LocExitOnFailure(hr, "Failed to get control X attribute."); - - // Y - pLocControl->nY = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pLocControl->nY)); - LocExitOnFailure(hr, "Failed to get control Y attribute."); - - // Width - pLocControl->nWidth = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pLocControl->nWidth)); - LocExitOnFailure(hr, "Failed to get control width attribute."); - - // Height - pLocControl->nHeight = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pLocControl->nHeight)); - LocExitOnFailure(hr, "Failed to get control height attribute."); - - // Text - hr = XmlGetText(pixn, &bstrText); - LocExitOnFailure(hr, "Failed to get control text in Wxl file."); - - hr = StrAllocString(&pLocControl->wzText, bstrText, 0); - LocExitOnFailure(hr, "Failed to duplicate control text in Wxl file."); - -LExit: - ReleaseBSTR(bstrText); - - return hr; -} diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp deleted file mode 100644 index ac68036a..00000000 --- a/src/dutil/logutil.cpp +++ /dev/null @@ -1,961 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define LoguExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) -#define LoguExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) -#define LoguExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) -#define LoguExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) -#define LoguExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOGUTIL, e, x, s, __VA_ARGS__) -#define LoguExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOGUTIL, g, x, s, __VA_ARGS__) - -// globals -static HMODULE LogUtil_hModule = NULL; -static BOOL LogUtil_fDisabled = FALSE; -static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE; -static LPWSTR LogUtil_sczLogPath = NULL; -static LPSTR LogUtil_sczPreInitBuffer = NULL; -static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD; -static CRITICAL_SECTION LogUtil_csLog = { }; -static BOOL LogUtil_fInitializedCriticalSection = FALSE; - -// Customization of certain parts of the string, within a line -static LPWSTR LogUtil_sczSpecialBeginLine = NULL; -static LPWSTR LogUtil_sczSpecialEndLine = NULL; -static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL; - -static LPCSTR LOGUTIL_UNKNOWN = "unknown"; -static LPCSTR LOGUTIL_WARNING = "warning"; -static LPCSTR LOGUTIL_STANDARD = "standard"; -static LPCSTR LOGUTIL_VERBOSE = "verbose"; -static LPCSTR LOGUTIL_DEBUG = "debug"; -static LPCSTR LOGUTIL_NONE = "none"; - -// prototypes -static HRESULT LogIdWork( - __in REPORT_LEVEL rl, - __in_opt HMODULE hModule, - __in DWORD dwLogId, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ); -static HRESULT LogStringWorkArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ); -static HRESULT LogStringWork( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_z LPCWSTR sczString, - __in BOOL fLOGUTIL_NEWLINE - ); - -// Hook to allow redirecting LogStringWorkRaw function calls -static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL; -static LPVOID s_vpvLogStringWorkRawContext = NULL; - - -/******************************************************************** - IsLogInitialized - Checks if log is currently initialized. -********************************************************************/ -extern "C" BOOL DAPI IsLogInitialized() -{ - return LogUtil_fInitializedCriticalSection; -} - -/******************************************************************** - IsLogOpen - Checks if log is currently initialized and open. -********************************************************************/ -extern "C" BOOL DAPI IsLogOpen() -{ - return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath); -} - - -/******************************************************************** - LogInitialize - initializes the logutil API - -********************************************************************/ -extern "C" void DAPI LogInitialize( - __in HMODULE hModule - ) -{ - AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called."); - - LogUtil_hModule = hModule; - LogUtil_fDisabled = FALSE; - - ::InitializeCriticalSection(&LogUtil_csLog); - LogUtil_fInitializedCriticalSection = TRUE; -} - - -/******************************************************************** - LogOpen - creates an application log file - - NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name -********************************************************************/ -extern "C" HRESULT DAPI LogOpen( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzLog, - __in_z_opt LPCWSTR wzPostfix, - __in_z_opt LPCWSTR wzExt, - __in BOOL fAppend, - __in BOOL fHeader, - __out_z_opt LPWSTR* psczLogPath - ) -{ - HRESULT hr = S_OK; - BOOL fEnteredCriticalSection = FALSE; - LPWSTR sczLogDirectory = NULL; - - ::EnterCriticalSection(&LogUtil_csLog); - fEnteredCriticalSection = TRUE; - - if (wzExt && *wzExt) - { - hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog); - LoguExitOnFailure(hr, "Failed to create log based on current system time."); - } - else - { - hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath); - LoguExitOnFailure(hr, "Failed to combine the log path."); - - hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory); - LoguExitOnFailure(hr, "Failed to get log directory."); - - hr = DirEnsureExists(sczLogDirectory, NULL); - LoguExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory); - - LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == LogUtil_hLog) - { - LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); - } - - if (fAppend) - { - ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); - } - } - - LogUtil_fDisabled = FALSE; - - if (fHeader) - { - LogHeader(); - } - - if (NULL != LogUtil_sczPreInitBuffer) - { - // Log anything that was logged before LogOpen() was called. - LogStringWorkRaw(LogUtil_sczPreInitBuffer); - ReleaseNullStr(LogUtil_sczPreInitBuffer); - } - - if (psczLogPath) - { - hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0); - LoguExitOnFailure(hr, "Failed to copy log path."); - } - -LExit: - if (fEnteredCriticalSection) - { - ::LeaveCriticalSection(&LogUtil_csLog); - } - - ReleaseStr(sczLogDirectory); - - return hr; -} - - -/******************************************************************** - LogDisable - closes any open files and disables in memory logging. - -********************************************************************/ -void DAPI LogDisable() -{ - ::EnterCriticalSection(&LogUtil_csLog); - - LogUtil_fDisabled = TRUE; - - ReleaseFileHandle(LogUtil_hLog); - ReleaseNullStr(LogUtil_sczLogPath); - ReleaseNullStr(LogUtil_sczPreInitBuffer); - - ::LeaveCriticalSection(&LogUtil_csLog); -} - - -/******************************************************************** - LogRedirect - Redirects all logging strings to the specified - function - or set NULL to disable the hook -********************************************************************/ -void DAPI LogRedirect( - __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, - __in_opt LPVOID pvContext - ) -{ - s_vpfLogStringWorkRaw = vpfLogStringWorkRaw; - s_vpvLogStringWorkRawContext = pvContext; -} - - -/******************************************************************** - LogRename - Renames a logfile, moving its contents to a new path, - and re-opening the file for appending at the new - location -********************************************************************/ -HRESULT DAPI LogRename( - __in_z LPCWSTR wzNewPath - ) -{ - HRESULT hr = S_OK; - BOOL fEnteredCriticalSection = FALSE; - - ::EnterCriticalSection(&LogUtil_csLog); - fEnteredCriticalSection = TRUE; - - ReleaseFileHandle(LogUtil_hLog); - - hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE); - LoguExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath); - - hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0); - LoguExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath); - - LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == LogUtil_hLog) - { - LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); - } - - // Enable "append" mode by moving file pointer to the end - ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); - -LExit: - if (fEnteredCriticalSection) - { - ::LeaveCriticalSection(&LogUtil_csLog); - } - - return hr; -} - - -extern "C" void DAPI LogClose( - __in BOOL fFooter - ) -{ - if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter) - { - LogFooter(); - } - - ReleaseFileHandle(LogUtil_hLog); - ReleaseNullStr(LogUtil_sczLogPath); - ReleaseNullStr(LogUtil_sczPreInitBuffer); -} - - -extern "C" void DAPI LogUninitialize( - __in BOOL fFooter - ) -{ - LogClose(fFooter); - - if (LogUtil_fInitializedCriticalSection) - { - ::DeleteCriticalSection(&LogUtil_csLog); - LogUtil_fInitializedCriticalSection = FALSE; - } - - LogUtil_hModule = NULL; - LogUtil_fDisabled = FALSE; - - ReleaseNullStr(LogUtil_sczSpecialBeginLine); - ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); - ReleaseNullStr(LogUtil_sczSpecialEndLine); -} - - -/******************************************************************** - LogIsOpen - returns whether log file is open or note - -********************************************************************/ -extern "C" BOOL DAPI LogIsOpen() -{ - return INVALID_HANDLE_VALUE != LogUtil_hLog; -} - - -/******************************************************************** - LogSetSpecialParams - sets a special beginline string, endline - string, post-timestamp string, etc. -********************************************************************/ -HRESULT DAPI LogSetSpecialParams( - __in_z_opt LPCWSTR wzSpecialBeginLine, - __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, - __in_z_opt LPCWSTR wzSpecialEndLine - ) -{ - HRESULT hr = S_OK; - - // Handle special string to be prepended before every full line - if (NULL == wzSpecialBeginLine) - { - ReleaseNullStr(LogUtil_sczSpecialBeginLine); - } - else - { - hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0); - LoguExitOnFailure(hr, "Failed to allocate copy of special beginline string"); - } - - // Handle special string to be appended to every time stamp - if (NULL == wzSpecialAfterTimeStamp) - { - ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); - } - else - { - hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0); - LoguExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string"); - } - - // Handle special string to be appended before every full line - if (NULL == wzSpecialEndLine) - { - ReleaseNullStr(LogUtil_sczSpecialEndLine); - } - else - { - hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0); - LoguExitOnFailure(hr, "Failed to allocate copy of special endline string"); - } - -LExit: - return hr; -} - -/******************************************************************** - LogSetLevel - sets the logging level - - NOTE: returns previous logging level -********************************************************************/ -extern "C" REPORT_LEVEL DAPI LogSetLevel( - __in REPORT_LEVEL rl, - __in BOOL fLogChange - ) -{ - AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set"); - - REPORT_LEVEL rlPrev = LogUtil_rlCurrent; - - if (LogUtil_rlCurrent != rl) - { - LogUtil_rlCurrent = rl; - - if (fLogChange) - { - LPCSTR szLevel = LOGUTIL_UNKNOWN; - switch (LogUtil_rlCurrent) - { - case REPORT_WARNING: - szLevel = LOGUTIL_WARNING; - break; - case REPORT_STANDARD: - szLevel = LOGUTIL_STANDARD; - break; - case REPORT_VERBOSE: - szLevel = LOGUTIL_VERBOSE; - break; - case REPORT_DEBUG: - szLevel = LOGUTIL_DEBUG; - break; - case REPORT_NONE: - szLevel = LOGUTIL_NONE; - break; - } - - LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); - } - } - - return rlPrev; -} - - -/******************************************************************** - LogGetLevel - gets the current logging level - -********************************************************************/ -extern "C" REPORT_LEVEL DAPI LogGetLevel() -{ - return LogUtil_rlCurrent; -} - - -/******************************************************************** - LogGetPath - gets the current log path - -********************************************************************/ -extern "C" HRESULT DAPI LogGetPath( - __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, - __in DWORD cchLogPath - ) -{ - Assert(pwzLogPath); - - HRESULT hr = S_OK; - - if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one! - { - ExitFunction1(hr = E_UNEXPECTED); - } - - hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath); - -LExit: - return hr; -} - - -/******************************************************************** - LogGetHandle - gets the current log file handle - -********************************************************************/ -extern "C" HANDLE DAPI LogGetHandle() -{ - return LogUtil_hLog; -} - - -/******************************************************************** - LogString - write a string to the log - - NOTE: use printf formatting ("%ls", "%d", etc.) -********************************************************************/ -extern "C" HRESULT DAPIV LogString( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - va_list args; - - va_start(args, szFormat); - hr = LogStringArgs(rl, szFormat, args); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogStringArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - hr = LogStringWorkArgs(rl, szFormat, args, FALSE); - -LExit: - return hr; -} - -/******************************************************************** - LogStringLine - write a string plus LOGUTIL_NEWLINE to the log - - NOTE: use printf formatting ("%ls", "%d", etc.) -********************************************************************/ -extern "C" HRESULT DAPIV LogStringLine( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - va_list args; - - va_start(args, szFormat); - hr = LogStringLineArgs(rl, szFormat, args); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogStringLineArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - hr = LogStringWorkArgs(rl, szFormat, args, TRUE); - -LExit: - return hr; -} - -/******************************************************************** - LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log - - NOTE: uses format string from MESSAGETABLE resource -********************************************************************/ - -extern "C" HRESULT DAPI LogIdModuleArgs( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in va_list args - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI LogIdModule( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - ... - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - va_list args; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - va_start(args, hModule); - hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); - va_end(args); - -LExit: - return hr; -} - - - - -/******************************************************************** - LogError - write an error to the log - - NOTE: use printf formatting ("%ls", "%d", etc.) -********************************************************************/ -extern "C" HRESULT DAPIV LogErrorString( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - - va_list args; - va_start(args, szFormat); - hr = LogErrorStringArgs(hrError, szFormat, args); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogErrorStringArgs( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFormat = NULL; - LPWSTR sczMessage = NULL; - - hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); - LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); - - // format the string as a unicode string - this is necessary to be able to include - // international characters in our output string. This does have the counterintuitive effect - // that the caller's "%s" is interpreted differently - // (so callers should use %hs for LPSTR and %ls for LPWSTR) - hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); - LoguExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat); - - hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage); - -LExit: - ReleaseStr(sczFormat); - ReleaseStr(sczMessage); - - return hr; -} - - -/******************************************************************** - LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log - - NOTE: uses format string from MESSAGETABLE resource - can log no more than three strings in the error message -********************************************************************/ -extern "C" HRESULT DAPI LogErrorIdModule( - __in HRESULT hrError, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzString1 = NULL, - __in_z_opt LPCWSTR wzString2 = NULL, - __in_z_opt LPCWSTR wzString3 = NULL - ) -{ - HRESULT hr = S_OK; - WCHAR wzError[11]; - WORD cStrings = 1; // guaranteed wzError is in the list - - hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError); - LoguExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError); - - cStrings += wzString1 ? 1 : 0; - cStrings += wzString2 ? 1 : 0; - cStrings += wzString3 ? 1 : 0; - - hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3); - LoguExitOnFailure(hr, "Failed to log id module."); - -LExit: - return hr; -} - -/******************************************************************** - LogHeader - write a standard header to the log - -********************************************************************/ -extern "C" HRESULT DAPI LogHeader() -{ - HRESULT hr = S_OK; - WCHAR wzComputerName[MAX_PATH]; - DWORD cchComputerName = countof(wzComputerName); - WCHAR wzPath[MAX_PATH]; - DWORD dwMajorVersion = 0; - DWORD dwMinorVersion = 0; - LPCSTR szLevel = LOGUTIL_UNKNOWN; - LPWSTR sczCurrentDateTime = NULL; - - // - // get the interesting data - // - if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath))) - { - memset(wzPath, 0, sizeof(wzPath)); - } - - hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion); - if (FAILED(hr)) - { - dwMajorVersion = 0; - dwMinorVersion = 0; - } - - if (!::GetComputerNameW(wzComputerName, &cchComputerName)) - { - ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName)); - } - - TimeCurrentDateTime(&sczCurrentDateTime, FALSE); - - // - // write data to the log - // - LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime); - LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF); - LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName); - switch (LogUtil_rlCurrent) - { - case REPORT_WARNING: - szLevel = LOGUTIL_WARNING; - break; - case REPORT_STANDARD: - szLevel = LOGUTIL_STANDARD; - break; - case REPORT_VERBOSE: - szLevel = LOGUTIL_VERBOSE; - break; - case REPORT_DEBUG: - szLevel = LOGUTIL_DEBUG; - break; - case REPORT_NONE: - szLevel = LOGUTIL_NONE; - break; - } - LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); - - hr = S_OK; - - ReleaseStr(sczCurrentDateTime); - - return hr; -} - - -/******************************************************************** - LogFooterWork - write a standard footer to the log - -********************************************************************/ - -static HRESULT LogFooterWork( - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - - va_list args; - va_start(args, szFormat); - hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogFooter() -{ - HRESULT hr = S_OK; - LPWSTR sczCurrentDateTime = NULL; - TimeCurrentDateTime(&sczCurrentDateTime, FALSE); - hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime); - ReleaseStr(sczCurrentDateTime); - return hr; -} - -/******************************************************************** - LogStringWorkRaw - Write a raw, unformatted string to the log - -********************************************************************/ -extern "C" HRESULT LogStringWorkRaw( - __in_z LPCSTR szLogData - ) -{ - Assert(szLogData && *szLogData); - - HRESULT hr = S_OK; - size_t cchLogData = 0; - DWORD cbLogData = 0; - DWORD cbTotal = 0; - DWORD cbWrote = 0; - - hr = ::StringCchLengthA(szLogData, STRSAFE_MAX_CCH, &cchLogData); - LoguExitOnRootFailure(hr, "Failed to get length of raw string"); - - cbLogData = (DWORD)cchLogData; - - // If the log hasn't been initialized yet, store it in a buffer - if (INVALID_HANDLE_VALUE == LogUtil_hLog) - { - hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0); - LoguExitOnFailure(hr, "Failed to concatenate string to pre-init buffer"); - - ExitFunction1(hr = S_OK); - } - - // write the string - while (cbTotal < cbLogData) - { - if (!::WriteFile(LogUtil_hLog, reinterpret_cast(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL)) - { - LoguExitOnLastError(hr, "Failed to write output to log: %ls - %hs", LogUtil_sczLogPath, szLogData); - } - - cbTotal += cbWrote; - } - -LExit: - return hr; -} - -// -// private worker functions -// -static HRESULT LogIdWork( - __in REPORT_LEVEL rl, - __in_opt HMODULE hModule, - __in DWORD dwLogId, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ) -{ - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - DWORD cch = 0; - - // get the string for the id -#pragma prefast(push) -#pragma prefast(disable:25028) -#pragma prefast(disable:25068) - cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, - static_cast(hModule), dwLogId, 0, reinterpret_cast(&pwz), 0, &args); -#pragma prefast(pop) - - if (0 == cch) - { - LoguExitOnLastError(hr, "failed to log id: %d", dwLogId); - } - - if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1]) - { - pwz[cch-2] = L'\0'; // remove newline from message table - } - - LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE); - -LExit: - if (pwz) - { - ::LocalFree(pwz); - } - - return hr; -} - - -static HRESULT LogStringWorkArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ) -{ - Assert(szFormat && *szFormat); - - HRESULT hr = S_OK; - LPWSTR sczFormat = NULL; - LPWSTR sczMessage = NULL; - - hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); - LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); - - // format the string as a unicode string - hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); - LoguExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat); - - hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE); - LoguExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage); - -LExit: - ReleaseStr(sczFormat); - ReleaseStr(sczMessage); - - return hr; -} - - -static HRESULT LogStringWork( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_z LPCWSTR sczString, - __in BOOL fLOGUTIL_NEWLINE - ) -{ - Assert(sczString && *sczString); - - HRESULT hr = S_OK; - BOOL fEnteredCriticalSection = FALSE; - LPWSTR scz = NULL; - LPCWSTR wzLogData = NULL; - LPSTR sczMultiByte = NULL; - - // If logging is disabled, just bail. - if (LogUtil_fDisabled) - { - ExitFunction(); - } - - ::EnterCriticalSection(&LogUtil_csLog); - fEnteredCriticalSection = TRUE; - - if (fLOGUTIL_NEWLINE) - { - // get the process and thread id. - DWORD dwProcessId = ::GetCurrentProcessId(); - DWORD dwThreadId = ::GetCurrentThreadId(); - - // get the time relative to GMT. - SYSTEMTIME st = { }; - ::GetLocalTime(&st); - - DWORD dwId = dwLogId & 0xFFFFFFF; - DWORD dwType = dwLogId & 0xF0000000; - LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i"; - - // add line prefix and trailing newline - hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"", - dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId, - LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n"); - LoguExitOnFailure(hr, "Failed to format line prefix."); - } - - wzLogData = scz ? scz : sczString; - - // Convert to UTF-8 before writing out to the log file - hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8); - LoguExitOnFailure(hr, "Failed to convert log string to UTF-8"); - - if (s_vpfLogStringWorkRaw) - { - hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext); - LoguExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString); - } - else - { - hr = LogStringWorkRaw(sczMultiByte); - LoguExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString); - } - -LExit: - if (fEnteredCriticalSection) - { - ::LeaveCriticalSection(&LogUtil_csLog); - } - - ReleaseStr(scz); - ReleaseStr(sczMultiByte); - - return hr; -} diff --git a/src/dutil/memutil.cpp b/src/dutil/memutil.cpp deleted file mode 100644 index c805a9c0..00000000 --- a/src/dutil/memutil.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define MemExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) -#define MemExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) -#define MemExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) -#define MemExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) -#define MemExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MEMUTIL, e, x, s, __VA_ARGS__) -#define MemExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MEMUTIL, g, x, s, __VA_ARGS__) - - -#if DEBUG -static BOOL vfMemInitialized = FALSE; -#endif - -extern "C" HRESULT DAPI MemInitialize() -{ -#if DEBUG - vfMemInitialized = TRUE; -#endif - return S_OK; -} - -extern "C" void DAPI MemUninitialize() -{ -#if DEBUG - vfMemInitialized = FALSE; -#endif -} - -extern "C" LPVOID DAPI MemAlloc( - __in SIZE_T cbSize, - __in BOOL fZero - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - AssertSz(0 < cbSize, "MemAlloc() called with invalid size"); - return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize); -} - - -extern "C" LPVOID DAPI MemReAlloc( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - AssertSz(0 < cbSize, "MemReAlloc() called with invalid size"); - return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize); -} - - -extern "C" HRESULT DAPI MemReAllocSecure( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero, - __deref_out LPVOID* ppvNew - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer"); - AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size"); - - HRESULT hr = S_OK; - DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY; - LPVOID pvNew = NULL; - - dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0; - pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize); - if (!pvNew) - { - pvNew = MemAlloc(cbSize, fZero); - if (pvNew) - { - const SIZE_T cbCurrent = MemSize(pv); - if (-1 == cbCurrent) - { - MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); - } - - // HeapReAlloc may allocate more memory than requested. - const SIZE_T cbNew = MemSize(pvNew); - if (-1 == cbNew) - { - MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); - } - - cbSize = cbNew; - if (cbSize > cbCurrent) - { - cbSize = cbCurrent; - } - - memcpy_s(pvNew, cbNew, pv, cbSize); - - SecureZeroMemory(pv, cbCurrent); - MemFree(pv); - } - } - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory"); - - *ppvNew = pvNew; - pvNew = NULL; - -LExit: - ReleaseMem(pvNew); - - return hr; -} - - -extern "C" HRESULT DAPI MemAllocArray( - __inout LPVOID* ppvArray, - __in SIZE_T cbArrayType, - __in DWORD dwItemCount - ) -{ - return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount); -} - - -extern "C" HRESULT DAPI MemReAllocArray( - __inout LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwNewItemCount - ) -{ - HRESULT hr = S_OK; - DWORD cNew = 0; - LPVOID pvNew = NULL; - SIZE_T cbNew = 0; - - hr = ::DWordAdd(cArray, dwNewItemCount, &cNew); - MemExitOnFailure(hr, "Integer overflow when calculating new element count."); - - hr = ::SIZETMult(cNew, cbArrayType, &cbNew); - MemExitOnFailure(hr, "Integer overflow when calculating new block size."); - - if (*ppvArray) - { - SIZE_T cbCurrent = MemSize(*ppvArray); - if (cbCurrent < cbNew) - { - pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array."); - - *ppvArray = pvNew; - } - } - else - { - pvNew = MemAlloc(cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); - - *ppvArray = pvNew; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI MemEnsureArraySize( - __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ) -{ - HRESULT hr = S_OK; - DWORD cNew = 0; - LPVOID pvNew = NULL; - SIZE_T cbNew = 0; - - hr = ::DWordAdd(cArray, dwGrowthCount, &cNew); - MemExitOnFailure(hr, "Integer overflow when calculating new element count."); - - hr = ::SIZETMult(cNew, cbArrayType, &cbNew); - MemExitOnFailure(hr, "Integer overflow when calculating new block size."); - - if (*ppvArray) - { - SIZE_T cbUsed = cArray * cbArrayType; - SIZE_T cbCurrent = MemSize(*ppvArray); - if (cbCurrent < cbUsed) - { - pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger."); - - *ppvArray = pvNew; - } - } - else - { - pvNew = MemAlloc(cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); - - *ppvArray = pvNew; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI MemInsertIntoArray( - __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, - __in DWORD dwInsertIndex, - __in DWORD cInsertItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ) -{ - HRESULT hr = S_OK; - DWORD i; - BYTE *pbArray = NULL; - - if (0 == cInsertItems) - { - ExitFunction1(hr = S_OK); - } - - hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount); - MemExitOnFailure(hr, "Failed to resize array while inserting items"); - - pbArray = reinterpret_cast(*ppvArray); - for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i) - { - memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType); - } - - // Zero out the newly-inserted items - memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType); - -LExit: - return hr; -} - -extern "C" void DAPI MemRemoveFromArray( - __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, - __in DWORD dwRemoveIndex, - __in DWORD cRemoveItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in BOOL fPreserveOrder - ) -{ - BYTE *pbArray = static_cast(pvArray); - DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex); - - if (fPreserveOrder) - { - memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType); - } - else - { - DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems); - memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType); - } - - ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType); -} - -extern "C" void DAPI MemArraySwapItems( - __inout_bcount(cbArrayType) LPVOID pvArray, - __in DWORD dwIndex1, - __in DWORD dwIndex2, - __in SIZE_T cbArrayType - ) -{ - BYTE *pbArrayItem1 = static_cast(pvArray) + dwIndex1 * cbArrayType; - BYTE *pbArrayItem2 = static_cast(pvArray) + dwIndex2 * cbArrayType; - DWORD dwByteIndex = 0; - - if (dwIndex1 == dwIndex2) - { - return; - } - - // Use XOR swapping to avoid the need for a temporary item - while (dwByteIndex < cbArrayType) - { - // Try to do many bytes at a time in most cases - if (cbArrayType - dwByteIndex > sizeof(DWORD64)) - { - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // y: X xor Y - *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - - dwByteIndex += sizeof(DWORD64); - } - else - { - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // y: X xor Y - *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - - dwByteIndex += sizeof(unsigned char); - } - } -} - -extern "C" HRESULT DAPI MemFree( - __in LPVOID pv - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()); -} - - -extern "C" SIZE_T DAPI MemSize( - __in LPCVOID pv - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - return ::HeapSize(::GetProcessHeap(), 0, pv); -} diff --git a/src/dutil/metautil.cpp b/src/dutil/metautil.cpp deleted file mode 100644 index f313fc1c..00000000 --- a/src/dutil/metautil.cpp +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// okay, this may look a little weird, but metautil.h cannot be in the -// pre-compiled header because we need to #define these things so the -// correct GUID's get pulled into this object file -#include -#include "metautil.h" - - -// Exit macros -#define MetaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) -#define MetaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) -#define MetaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) -#define MetaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) -#define MetaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_METAUTIL, e, x, s, __VA_ARGS__) -#define MetaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_METAUTIL, g, x, s, __VA_ARGS__) - - -// prototypes -static void Sort( - __in_ecount(cArray) DWORD dwArray[], - __in int cArray - ); - - -/******************************************************************** - MetaFindWebBase - finds a metabase base string that matches IP, Port and Header - -********************************************************************/ -extern "C" HRESULT DAPI MetaFindWebBase( - __in IMSAdminBaseW* piMetabase, - __in_z LPCWSTR wzIP, - __in int iPort, - __in_z LPCWSTR wzHeader, - __in BOOL fSecure, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ) -{ - Assert(piMetabase && cchWebBase); - - HRESULT hr = S_OK; - - BOOL fFound = FALSE; - - WCHAR wzKey[METADATA_MAX_NAME_LEN]; - WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; - DWORD dwIndex = 0; - - METADATA_RECORD mr; - METADATA_RECORD mrAddress; - - LPWSTR pwzExists = NULL; - LPWSTR pwzIPExists = NULL; - LPWSTR pwzPortExists = NULL; - int iPortExists = 0; - LPCWSTR pwzHeaderExists = NULL; - - memset(&mr, 0, sizeof(mr)); - mr.dwMDIdentifier = MD_KEY_TYPE; - mr.dwMDAttributes = METADATA_INHERIT; - mr.dwMDUserType = IIS_MD_UT_SERVER; - mr.dwMDDataType = ALL_METADATA; - - memset(&mrAddress, 0, sizeof(mrAddress)); - mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS; - mrAddress.dwMDAttributes = METADATA_INHERIT; - mrAddress.dwMDUserType = IIS_MD_UT_SERVER; - mrAddress.dwMDDataType = ALL_METADATA; - - // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb - for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) - { - hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); - if (FAILED(hr)) - break; - - ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); - hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); - if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) - { - hr = S_FALSE; // didn't find anything, try next one - continue; - } - MetaExitOnFailure(hr, "failed to get key from metabase while searching for web servers"); - - // if we have an IIsWebServer store the key - if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) - { - hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress); - if (MD_ERROR_DATA_NOT_FOUND == hr) - hr = S_FALSE; - MetaExitOnFailure(hr, "failed to get address from metabase while searching for web servers"); - - // break down the first address into parts - pwzIPExists = reinterpret_cast(mrAddress.pbMDData); - pwzExists = wcsstr(pwzIPExists, L":"); - if (NULL == pwzExists) - continue; - - *pwzExists = L'\0'; - - pwzPortExists = pwzExists + 1; - pwzExists = wcsstr(pwzPortExists, L":"); - if (NULL == pwzExists) - continue; - - *pwzExists = L'\0'; - iPortExists = wcstol(pwzPortExists, NULL, 10); - - pwzHeaderExists = pwzExists + 1; - - // compare the passed in address with the address listed for this web - if (S_OK == hr && - (0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) && - iPort == iPortExists && - 0 == lstrcmpW(wzHeader, pwzHeaderExists)) - { - // if the passed in buffer wasn't big enough - hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey); - MetaExitOnFailure(hr, "failed to copy in web base: %ls", wzKey); - - fFound = TRUE; - break; - } - } - } - - if (E_NOMOREITEMS == hr) - { - Assert(!fFound); - hr = S_FALSE; - } - -LExit: - MetaFreeValue(&mrAddress); - MetaFreeValue(&mr); - - if (!fFound && SUCCEEDED(hr)) - hr = S_FALSE; - - return hr; -} - - -/******************************************************************** - MetaFindFreeWebBase - finds the next metabase base string - -********************************************************************/ -extern "C" HRESULT DAPI MetaFindFreeWebBase( - __in IMSAdminBaseW* piMetabase, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ) -{ - Assert(piMetabase); - - HRESULT hr = S_OK; - - WCHAR wzKey[METADATA_MAX_NAME_LEN]; - WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; - DWORD dwSubKeys[100]; - int cSubKeys = 0; - DWORD dwIndex = 0; - - int i; - DWORD dwKey; - - METADATA_RECORD mr; - - memset(&mr, 0, sizeof(mr)); - mr.dwMDIdentifier = MD_KEY_TYPE; - mr.dwMDAttributes = 0; - mr.dwMDUserType = IIS_MD_UT_SERVER; - mr.dwMDDataType = STRING_METADATA; - - // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb - for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) - { - hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); - if (FAILED(hr)) - break; - - ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); - - hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); - if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) - { - hr = S_FALSE; // didn't find anything, try next one - continue; - } - MetaExitOnFailure(hr, "failed to get key from metabase while searching for free web root"); - - // if we have a IIsWebServer get the address information - if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast(mr.pbMDData))) - { - if (cSubKeys >= countof(dwSubKeys)) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - MetaExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites"); - } - - dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10); - ++cSubKeys; - Sort(dwSubKeys, cSubKeys); - } - } - - if (E_NOMOREITEMS == hr) - hr = S_OK; - MetaExitOnFailure(hr, "failed to find free web root"); - - // find the lowest free web root - dwKey = 1; - for (i = 0; i < cSubKeys; ++i) - { - if (dwKey < dwSubKeys[i]) - break; - - dwKey = dwSubKeys[i] + 1; - } - - hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey); -LExit: - MetaFreeValue(&mr); - return hr; -} - - -/******************************************************************** - MetaOpenKey - open key - -********************************************************************/ -extern "C" HRESULT DAPI MetaOpenKey( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __in DWORD dwAccess, - __in DWORD cRetries, - __out METADATA_HANDLE* pmh - ) -{ - Assert(piMetabase && pmh); - - HRESULT hr = S_OK; - - // loop while the key is busy - do - { - hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh); - if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr) - ::SleepEx(1000, TRUE); - } while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--); - - return hr; -} - - -/******************************************************************** - MetaGetValue - finds the next metabase base string - - NOTE: piMetabase is optional -********************************************************************/ -extern "C" HRESULT DAPI MetaGetValue( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __inout METADATA_RECORD* pmr - ) -{ - Assert(pmr); - - HRESULT hr = S_OK; - BOOL fInitialized = FALSE; - DWORD cbRequired = 0; - - if (!piMetabase) - { - hr = ::CoInitialize(NULL); - MetaExitOnFailure(hr, "failed to initialize COM"); - fInitialized = TRUE; - - hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); - MetaExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object"); - } - - if (!pmr->pbMDData) - { - pmr->dwMDDataLen = 256; - pmr->pbMDData = static_cast(MemAlloc(pmr->dwMDDataLen, TRUE)); - MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); - } - else // set the size of the data to the actual size of the memory - { - SIZE_T cb = MemSize(pmr->pbMDData); - if (cb > DWORD_MAX) - { - MetaExitOnRootFailure(hr = E_INVALIDSTATE, "metabase data is too large: %Iu", cb); - } - pmr->dwMDDataLen = (DWORD)cb; - } - - hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); - if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) - { - pmr->dwMDDataLen = cbRequired; - BYTE* pb = static_cast(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE)); - MetaExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value"); - - pmr->pbMDData = pb; - hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); - } - MetaExitOnFailure(hr, "failed to get metabase data"); - -LExit: - if (fInitialized) - { - ReleaseObject(piMetabase); - ::CoUninitialize(); - } - - return hr; -} - - -/******************************************************************** - MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue() - - NOTE: METADATA_RECORD must have been returned from MetaGetValue() above -********************************************************************/ -extern "C" void DAPI MetaFreeValue( - __in METADATA_RECORD* pmr - ) -{ - Assert(pmr); - - ReleaseNullMem(pmr->pbMDData); -} - - -// -// private -// - -/******************************************************************** - Sort - quick and dirty insertion sort - -********************************************************************/ -static void Sort( - __in_ecount(cArray) DWORD dwArray[], - __in int cArray - ) -{ - int i, j; - DWORD dwData; - - for (i = 1; i < cArray; ++i) - { - dwData = dwArray[i]; - - j = i - 1; - while (0 <= j && dwArray[j] > dwData) - { - dwArray[j + 1] = dwArray[j]; - j--; - } - - dwArray[j + 1] = dwData; - } -} diff --git a/src/dutil/monutil.cpp b/src/dutil/monutil.cpp deleted file mode 100644 index 6a7f0596..00000000 --- a/src/dutil/monutil.cpp +++ /dev/null @@ -1,2019 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define MonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) -#define MonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) -#define MonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) -#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) -#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__) -#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__) - -const int MON_THREAD_GROWTH = 5; -const int MON_ARRAY_GROWTH = 40; -const int MON_MAX_MONITORS_PER_THREAD = 63; -const int MON_THREAD_INIT_RETRIES = 1000; -const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10; -const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute -const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently -const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000; -const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass"; - -enum MON_MESSAGE -{ - MON_MESSAGE_ADD = WM_APP + 1, - MON_MESSAGE_REMOVE, - MON_MESSAGE_REMOVED, // Sent by waiter thread back to coordinator thread to indicate a remove occurred - 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). - 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. - MON_MESSAGE_NETWORK_STATUS_UPDATE, // Some change to network connectivity occurred (a network connection was connected or disconnected for example) - MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any successful network waits. - // Annoyingly, this is necessary to catch the rare case that the remote server goes offline unexpectedly, such as by - // network cable unplugged or power loss - in this case there is no local network status change, and the wait will just never fire. - // So we very occasionally retry all successful network waits. When this occurs, we notify for changes, even though there may not have been any. - // 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. - // 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). - MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any failed network waits - MON_MESSAGE_DRIVE_STATUS_UPDATE, // Some change to local drive has occurred (new drive created or plugged in, or removed) - MON_MESSAGE_DRIVE_QUERY_REMOVE, // User wants to unplug a drive, which MonUtil will always allow - MON_MESSAGE_STOP -}; - -enum MON_TYPE -{ - MON_NONE = 0, - MON_DIRECTORY = 1, - MON_REGKEY = 2 -}; - -struct MON_REQUEST -{ - MON_TYPE type; - DWORD dwMaxSilencePeriodInMs; - - // Handle to the main window for RegisterDeviceNotification() (same handle as owned by coordinator thread) - HWND hwnd; - // and handle to the notification (specific to this request) - HDEVNOTIFY hNotify; - - BOOL fRecursive; - void *pvContext; - - HRESULT hrStatus; - - LPWSTR sczOriginalPathRequest; - BOOL fNetwork; // This reflects either a UNC or mounted drive original request - DWORD dwPathHierarchyIndex; - LPWSTR *rgsczPathHierarchy; - DWORD cPathHierarchy; - - // 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 - // after notification, we set fPendingFire back to FALSE - BOOL fPendingFire; - BOOL fSkipDeltaAdd; - DWORD dwSilencePeriodInMs; - - union - { - struct - { - } directory; - struct - { - HKEY hkRoot; - HKEY hkSubKey; - REG_KEY_BITNESS kbKeyBitness; // Only used to pass on 32-bit, 64-bit, or default parameter - } regkey; - }; -}; - -struct MON_ADD_MESSAGE -{ - MON_REQUEST request; - HANDLE handle; -}; - -struct MON_REMOVE_MESSAGE -{ - MON_TYPE type; - BOOL fRecursive; - - union - { - struct - { - LPWSTR sczDirectory; - } directory; - struct - { - HKEY hkRoot; - LPWSTR sczSubKey; - REG_KEY_BITNESS kbKeyBitness; - } regkey; - }; -}; - -struct MON_WAITER_CONTEXT -{ - DWORD dwCoordinatorThreadId; - - HANDLE hWaiterThread; - DWORD dwWaiterThreadId; - BOOL fWaiterThreadMessageQueueInitialized; - - // Callbacks - PFN_MONGENERAL vpfMonGeneral; - PFN_MONDIRECTORY vpfMonDirectory; - PFN_MONREGKEY vpfMonRegKey; - - // Context for callbacks - LPVOID pvContext; - - // HANDLEs are in their own array for easy use with WaitForMultipleObjects() - // After initialization, the very first handle is just to wake the listener thread to have it re-wait on a new list - // 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 - HANDLE *rgHandles; - DWORD cHandles; - - // Requested things to monitor - MON_REQUEST *rgRequests; - DWORD cRequests; - - // Number of pending notifications - DWORD cRequestsPending; - - // Number of requests in a failed state (couldn't initiate wait) - DWORD cRequestsFailing; -}; - -// Info stored about each waiter by the coordinator -struct MON_WAITER_INFO -{ - DWORD cMonitorCount; - - MON_WAITER_CONTEXT *pWaiterContext; -}; - -// This struct is used when Thread A wants to send a task to another thread B (and get notified when the task finishes) -// 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 -// (even long after sender is no longer waiting, in case thread has huge message queue) -// and you must send 2 parameters in the message: -// 1) a pointer to this struct (which is always valid) -// 2) the original value of dwIteration -// The receiver of the message can compare the current value of dwSendIteration in the struct with what was sent in the message -// If values are different, we're too late and thread A is no longer waiting on this response -// otherwise, set dwResponseIteration to the same value, and call ::SetEvent() on hWait -// Thread A will then wakeup, and must verify that dwResponseIteration == dwSendIteration to ensure it isn't an earlier out-of-date reply -// replying to a newer wait -// pvContext is used to send a misc parameter related to processing data -struct MON_INTERNAL_TEMPORARY_WAIT -{ - // Should be incremented each time sender sends a pointer to this struct, so each request has a different iteration - DWORD dwSendIteration; - DWORD dwReceiveIteration; - HANDLE hWait; - void *pvContext; -}; - -struct MON_STRUCT -{ - HANDLE hCoordinatorThread; - DWORD dwCoordinatorThreadId; - BOOL fCoordinatorThreadMessageQueueInitialized; - - // Invisible window for receiving network status & drive added/removal messages - HWND hwnd; - // Used by window procedure for sending request and waiting for response from waiter threads - // such as in event of a request to remove a device - MON_INTERNAL_TEMPORARY_WAIT internalWait; - - // Callbacks - PFN_MONGENERAL vpfMonGeneral; - PFN_MONDRIVESTATUS vpfMonDriveStatus; - PFN_MONDIRECTORY vpfMonDirectory; - PFN_MONREGKEY vpfMonRegKey; - - // Context for callbacks - LPVOID pvContext; - - // Waiter thread array - MON_WAITER_INFO *rgWaiterThreads; - DWORD cWaiterThreads; -}; - -const int MON_HANDLE_BYTES = sizeof(MON_STRUCT); - -static DWORD WINAPI CoordinatorThread( - __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext - ); -// Initiates (or if *pHandle is non-null, continues) wait on the directory or subkey -// if the directory or subkey doesn't exist, instead calls it on the first existing parent directory or subkey -// writes to pRequest->dwPathHierarchyIndex with the array index that was waited on -static HRESULT InitiateWait( - __inout MON_REQUEST *pRequest, - __inout HANDLE *pHandle - ); -static DWORD WINAPI WaiterThread( - __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext - ); -static void Notify( - __in HRESULT hr, - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REQUEST *pRequest - ); -static void MonRequestDestroy( - __in MON_REQUEST *pRequest - ); -static void MonAddMessageDestroy( - __in_opt MON_ADD_MESSAGE *pMessage - ); -static void MonRemoveMessageDestroy( - __in_opt MON_REMOVE_MESSAGE *pMessage - ); -static BOOL GetRecursiveFlag( - __in MON_REQUEST *pRequest, - __in DWORD dwIndex - ); -static HRESULT FindRequestIndex( - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REMOVE_MESSAGE *pMessage, - __out DWORD *pdwIndex - ); -static HRESULT RemoveRequest( - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex - ); -static REGSAM GetRegKeyBitness( - __in MON_REQUEST *pRequest - ); -static HRESULT DuplicateRemoveMessage( - __in MON_REMOVE_MESSAGE *pMessage, - __out MON_REMOVE_MESSAGE **ppMessage - ); -static LRESULT CALLBACK MonWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static HRESULT CreateMonWindow( - __in MON_STRUCT *pm, - __out HWND *pHwnd - ); -// if *phMonitor is non-NULL, closes the old wait before re-starting the new wait -static HRESULT WaitForNetworkChanges( - __inout HANDLE *phMonitor, - __in MON_STRUCT *pm - ); -static HRESULT UpdateWaitStatus( - __in HRESULT hrNewStatus, - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex, - __out_opt DWORD *pdwNewRequestIndex - ); - -extern "C" HRESULT DAPI MonCreate( - __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, - __in PFN_MONGENERAL vpfMonGeneral, - __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, - __in_opt PFN_MONDIRECTORY vpfMonDirectory, - __in_opt PFN_MONREGKEY vpfMonRegKey, - __in_opt LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - DWORD dwRetries = MON_THREAD_INIT_RETRIES; - - MonExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor"); - - // Allocate the struct - *pHandle = static_cast(MemAlloc(sizeof(MON_STRUCT), TRUE)); - MonExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object"); - - MON_STRUCT *pm = static_cast(*pHandle); - - pm->vpfMonGeneral = vpfMonGeneral; - pm->vpfMonDriveStatus = vpfMonDriveStatus; - pm->vpfMonDirectory = vpfMonDirectory; - pm->vpfMonRegKey = vpfMonRegKey; - pm->pvContext = pvContext; - - pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId); - if (!pm->hCoordinatorThread) - { - MonExitWithLastError(hr, "Failed to create waiter thread."); - } - - // 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. - while (!pm->fCoordinatorThreadMessageQueueInitialized && 0 < dwRetries) - { - ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); - --dwRetries; - } - - if (0 == dwRetries) - { - hr = E_UNEXPECTED; - MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI MonAddDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzDirectory, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvDirectoryContext - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczDirectory = NULL; - LPWSTR sczOriginalPathRequest = NULL; - MON_ADD_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0); - MonExitOnFailure(hr, "Failed to convert directory string to UNC path"); - - hr = PathBackslashTerminate(&sczOriginalPathRequest); - MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\') - { - pMessage->request.fNetwork = TRUE; - } - else - { - hr = UncConvertFromMountedDrive(&sczDirectory, sczOriginalPathRequest); - if (SUCCEEDED(hr)) - { - pMessage->request.fNetwork = TRUE; - } - } - - if (NULL == sczDirectory) - { - // Likely not a mounted drive - just copy the request then - hr = S_OK; - - hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0); - MonExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest); - } - - pMessage->handle = INVALID_HANDLE_VALUE; - pMessage->request.type = MON_DIRECTORY; - pMessage->request.fRecursive = fRecursive; - pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs; - pMessage->request.hwnd = pm->hwnd; - pMessage->request.pvContext = pvDirectoryContext; - pMessage->request.sczOriginalPathRequest = sczOriginalPathRequest; - sczOriginalPathRequest = NULL; - - hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); - MonExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory); - - if (0 < pMessage->request.cPathHierarchy) - { - pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); - } - pMessage = NULL; - } - -LExit: - ReleaseStr(sczDirectory); - ReleaseStr(sczOriginalPathRequest); - MonAddMessageDestroy(pMessage); - - return hr; -} - -extern "C" HRESULT DAPI MonAddRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvRegKeyContext - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczSubKey = NULL; - MON_ADD_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczSubKey, wzSubKey, 0); - MonExitOnFailure(hr, "Failed to copy subkey string"); - - hr = PathBackslashTerminate(&sczSubKey); - MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL); - MonExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor"); - - pMessage->request.type = MON_REGKEY; - pMessage->request.regkey.hkRoot = hkRoot; - pMessage->request.regkey.kbKeyBitness = kbKeyBitness; - pMessage->request.fRecursive = fRecursive; - pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs, - pMessage->request.hwnd = pm->hwnd; - pMessage->request.pvContext = pvRegKeyContext; - - hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); - MonExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey); - - if (0 < pMessage->request.cPathHierarchy) - { - pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); - MonExitOnFailure(hr, "Failed to initiate wait"); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey); - } - pMessage = NULL; - } - -LExit: - ReleaseStr(sczSubKey); - MonAddMessageDestroy(pMessage); - - return hr; -} - -extern "C" HRESULT DAPI MonRemoveDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzDirectory, - __in BOOL fRecursive - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczDirectory = NULL; - MON_REMOVE_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczDirectory, wzDirectory, 0); - MonExitOnFailure(hr, "Failed to copy directory string"); - - hr = PathBackslashTerminate(&sczDirectory); - MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - pMessage->type = MON_DIRECTORY; - pMessage->fRecursive = fRecursive; - - hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0); - MonExitOnFailure(hr, "Failed to allocate copy of directory string"); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); - } - pMessage = NULL; - -LExit: - MonRemoveMessageDestroy(pMessage); - - return hr; -} - -extern "C" HRESULT DAPI MonRemoveRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczSubKey = NULL; - MON_REMOVE_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczSubKey, wzSubKey, 0); - MonExitOnFailure(hr, "Failed to copy subkey string"); - - hr = PathBackslashTerminate(&sczSubKey); - MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - pMessage->type = MON_REGKEY; - pMessage->regkey.hkRoot = hkRoot; - pMessage->regkey.kbKeyBitness = kbKeyBitness; - pMessage->fRecursive = fRecursive; - - hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0); - MonExitOnFailure(hr, "Failed to allocate copy of directory string"); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey); - } - pMessage = NULL; - -LExit: - ReleaseStr(sczSubKey); - MonRemoveMessageDestroy(pMessage); - - return hr; -} - -extern "C" void DAPI MonDestroy( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - MON_STRUCT *pm = static_cast(handle); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) - { - er = ::GetLastError(); - if (ERROR_INVALID_THREAD_ID == er) - { - // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up - er = ERROR_SUCCESS; - } - MonExitOnWin32Error(er, hr, "Failed to send message to background thread to halt"); - } - - if (pm->hCoordinatorThread) - { - ::WaitForSingleObject(pm->hCoordinatorThread, INFINITE); - ::CloseHandle(pm->hCoordinatorThread); - } - -LExit: - return; -} - -static void MonRequestDestroy( - __in MON_REQUEST *pRequest - ) -{ - if (NULL != pRequest) - { - if (MON_REGKEY == pRequest->type) - { - ReleaseRegKey(pRequest->regkey.hkSubKey); - } - else if (MON_DIRECTORY == pRequest->type && pRequest->hNotify) - { - UnregisterDeviceNotification(pRequest->hNotify); - pRequest->hNotify = NULL; - } - ReleaseStr(pRequest->sczOriginalPathRequest); - ReleaseStrArray(pRequest->rgsczPathHierarchy, pRequest->cPathHierarchy); - } -} - -static void MonAddMessageDestroy( - __in_opt MON_ADD_MESSAGE *pMessage - ) -{ - if (pMessage) - { - MonRequestDestroy(&pMessage->request); - if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle) - { - ::FindCloseChangeNotification(pMessage->handle); - } - else if (MON_REGKEY == pMessage->request.type) - { - ReleaseHandle(pMessage->handle); - } - - ReleaseMem(pMessage); - } -} - -static void MonRemoveMessageDestroy( - __in_opt MON_REMOVE_MESSAGE *pMessage - ) -{ - if (pMessage) - { - switch (pMessage->type) - { - case MON_DIRECTORY: - ReleaseStr(pMessage->directory.sczDirectory); - break; - case MON_REGKEY: - ReleaseStr(pMessage->regkey.sczSubKey); - break; - default: - Assert(false); - } - - ReleaseMem(pMessage); - } -} - -static DWORD WINAPI CoordinatorThread( - __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - MSG msg = { }; - DWORD dwThreadIndex = DWORD_MAX; - DWORD dwRetries; - DWORD dwFailingNetworkWaits = 0; - MON_WAITER_CONTEXT *pWaiterContext = NULL; - MON_REMOVE_MESSAGE *pRemoveMessage = NULL; - MON_REMOVE_MESSAGE *pTempRemoveMessage = NULL; - MON_STRUCT *pm = reinterpret_cast(pvContext); - WSADATA wsaData = { }; - HANDLE hMonitor = NULL; - BOOL fRet = FALSE; - UINT_PTR uTimerSuccessfulNetworkRetry = 0; - UINT_PTR uTimerFailedNetworkRetry = 0; - - // Ensure the thread has a message queue - ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - pm->fCoordinatorThreadMessageQueueInitialized = TRUE; - - hr = CreateMonWindow(pm, &pm->hwnd); - MonExitOnFailure(hr, "Failed to create window for status update thread"); - - ::WSAStartup(MAKEWORD(2, 2), &wsaData); - - hr = WaitForNetworkChanges(&hMonitor, pm); - MonExitOnFailure(hr, "Failed to wait for network changes"); - - uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL); - if (0 == uTimerSuccessfulNetworkRetry) - { - MonExitWithLastError(hr, "Failed to set timer for network successful retry"); - } - - while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) - { - if (-1 == fRet) - { - hr = E_UNEXPECTED; - MonExitOnRootFailure(hr, "Unexpected return value from message pump."); - } - else - { - switch (msg.message) - { - case MON_MESSAGE_ADD: - dwThreadIndex = DWORD_MAX; - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - if (pm->rgWaiterThreads[i].cMonitorCount < MON_MAX_MONITORS_PER_THREAD) - { - dwThreadIndex = i; - break; - } - } - - if (dwThreadIndex < pm->cWaiterThreads) - { - pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; - } - else - { - hr = MemEnsureArraySize(reinterpret_cast(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH); - MonExitOnFailure(hr, "Failed to grow waiter thread array size"); - ++pm->cWaiterThreads; - - dwThreadIndex = pm->cWaiterThreads - 1; - pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE)); - MonExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct"); - pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; - pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId(); - pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral; - pWaiterContext->vpfMonDirectory = pm->vpfMonDirectory; - pWaiterContext->vpfMonRegKey = pm->vpfMonRegKey; - pWaiterContext->pvContext = pm->pvContext; - - hr = MemEnsureArraySize(reinterpret_cast(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0); - MonExitOnFailure(hr, "Failed to allocate first handle"); - pWaiterContext->cHandles = 1; - - pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL); - MonExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event"); - - pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId); - if (!pWaiterContext->hWaiterThread) - { - MonExitWithLastError(hr, "Failed to create waiter thread."); - } - - dwRetries = MON_THREAD_INIT_RETRIES; - while (!pWaiterContext->fWaiterThreadMessageQueueInitialized && 0 < dwRetries) - { - ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); - --dwRetries; - } - - if (0 == dwRetries) - { - hr = E_UNEXPECTED; - MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); - } - } - - ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount; - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message"); - } - break; - - case MON_MESSAGE_REMOVE: - // Send remove to all waiter threads. They'll ignore it if they don't have that monitor. - // If they do have that monitor, they'll remove it from their list, and tell coordinator they have another - // empty slot via MON_MESSAGE_REMOVED message - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - pRemoveMessage = reinterpret_cast(msg.wParam); - - hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage); - MonExitOnFailure(hr, "Failed to duplicate remove message"); - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pTempRemoveMessage), msg.lParam)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); - } - pTempRemoveMessage = NULL; - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message"); - } - } - MonRemoveMessageDestroy(pRemoveMessage); - pRemoveMessage = NULL; - break; - - case MON_MESSAGE_REMOVED: - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - if (pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId == static_cast(msg.wParam)) - { - Assert(pm->rgWaiterThreads[i].cMonitorCount > 0); - --pm->rgWaiterThreads[i].cMonitorCount; - if (0 == pm->rgWaiterThreads[i].cMonitorCount) - { - if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to stop"); - } - MemRemoveFromArray(reinterpret_cast(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE); - --pm->cWaiterThreads; - --i; // reprocess this index in the for loop, which will now contain the item after the one we removed - } - } - } - break; - - case MON_MESSAGE_NETWORK_WAIT_FAILED: - if (0 == dwFailingNetworkWaits) - { - uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL); - if (0 == uTimerFailedNetworkRetry) - { - MonExitWithLastError(hr, "Failed to set timer for network fail retry"); - } - } - ++dwFailingNetworkWaits; - break; - - case MON_MESSAGE_NETWORK_WAIT_SUCCEEDED: - --dwFailingNetworkWaits; - if (0 == dwFailingNetworkWaits) - { - if (!::KillTimer(NULL, uTimerFailedNetworkRetry)) - { - MonExitWithLastError(hr, "Failed to kill timer for network fail retry"); - } - uTimerFailedNetworkRetry = 0; - } - break; - - case MON_MESSAGE_NETWORK_STATUS_UPDATE: - hr = WaitForNetworkChanges(&hMonitor, pm); - MonExitOnFailure(hr, "Failed to re-wait for network changes"); - - // Propagate any network status update messages to all waiter threads - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); - } - } - break; - - case WM_TIMER: - // Timer means some network wait is failing, and we need to retry every so often in case a remote server goes back up - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); - } - } - break; - - case MON_MESSAGE_DRIVE_STATUS_UPDATE: - // If user requested to be notified of drive status updates, notify! - if (pm->vpfMonDriveStatus) - { - pm->vpfMonDriveStatus(static_cast(msg.wParam), static_cast(msg.lParam), pm->pvContext); - } - - // Propagate any drive status update messages to all waiter threads - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message"); - } - } - break; - - case MON_MESSAGE_STOP: - ExitFunction1(hr = static_cast(msg.wParam)); - - default: - // This thread owns a window, so this handles all the other random messages we get - ::TranslateMessage(&msg); - ::DispatchMessageW(&msg); - break; - } - } - } - -LExit: - if (uTimerFailedNetworkRetry) - { - fRet = ::KillTimer(NULL, uTimerFailedNetworkRetry); - } - if (uTimerSuccessfulNetworkRetry) - { - fRet = ::KillTimer(NULL, uTimerSuccessfulNetworkRetry); - } - - if (pm->hwnd) - { - ::CloseWindow(pm->hwnd); - } - - // Tell all waiter threads to shutdown - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - if (NULL != pWaiterContext->rgHandles[0]) - { - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) - { - TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to waiter thread to stop"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify waiter thread of incoming message"); - } - } - } - - if (hMonitor != NULL) - { - ::WSALookupServiceEnd(hMonitor); - } - - // Now confirm they're actually shut down before returning - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - if (NULL != pWaiterContext->hWaiterThread) - { - ::WaitForSingleObject(pWaiterContext->hWaiterThread, INFINITE); - ::CloseHandle(pWaiterContext->hWaiterThread); - } - - // Waiter thread can't release these, because coordinator thread uses it to try communicating with waiter thread - ReleaseHandle(pWaiterContext->rgHandles[0]); - ReleaseMem(pWaiterContext->rgHandles); - - ReleaseMem(pWaiterContext); - } - - if (FAILED(hr)) - { - // If coordinator thread fails, notify general callback of an error - Assert(pm->vpfMonGeneral); - pm->vpfMonGeneral(hr, pm->pvContext); - } - MonRemoveMessageDestroy(pRemoveMessage); - MonRemoveMessageDestroy(pTempRemoveMessage); - - ::WSACleanup(); - - return hr; -} - -static HRESULT InitiateWait( - __inout MON_REQUEST *pRequest, - __inout HANDLE *pHandle - ) -{ - HRESULT hr = S_OK; - HRESULT hrTemp = S_OK; - DEV_BROADCAST_HANDLE dev = { }; - BOOL fRedo = FALSE; - BOOL fHandleFound; - DWORD er = ERROR_SUCCESS; - DWORD dwIndex = 0; - HKEY hk = NULL; - HANDLE hTemp = INVALID_HANDLE_VALUE; - - if (pRequest->hNotify) - { - UnregisterDeviceNotification(pRequest->hNotify); - pRequest->hNotify = NULL; - } - - do - { - fRedo = FALSE; - fHandleFound = FALSE; - - for (DWORD i = 0; i < pRequest->cPathHierarchy && !fHandleFound; ++i) - { - dwIndex = pRequest->cPathHierarchy - i - 1; - switch (pRequest->type) - { - case MON_DIRECTORY: - if (INVALID_HANDLE_VALUE != *pHandle) - { - ::FindCloseChangeNotification(*pHandle); - *pHandle = INVALID_HANDLE_VALUE; - } - - *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); - if (INVALID_HANDLE_VALUE == *pHandle) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || E_ACCESSDENIED == hr) - { - continue; - } - MonExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]); - } - else - { - fHandleFound = TRUE; - hr = S_OK; - } - break; - case MON_REGKEY: - ReleaseRegKey(pRequest->regkey.hkSubKey); - hr = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex], KEY_NOTIFY | GetRegKeyBitness(pRequest), &pRequest->regkey.hkSubKey); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - continue; - } - MonExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); - - er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE); - ReleaseRegKey(hk); - hr = HRESULT_FROM_WIN32(er); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || HRESULT_FROM_WIN32(ERROR_KEY_DELETED) == hr) - { - continue; - } - else - { - MonExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); - - fHandleFound = TRUE; - } - - break; - default: - return E_INVALIDARG; - } - } - - pRequest->dwPathHierarchyIndex = dwIndex; - - // 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. - // If it has, restart the whole loop - if (dwIndex < pRequest->cPathHierarchy - 1) - { - switch (pRequest->type) - { - case MON_DIRECTORY: - 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); - if (INVALID_HANDLE_VALUE != hTemp) - { - ::FindCloseChangeNotification(hTemp); - fRedo = TRUE; - } - break; - case MON_REGKEY: - hrTemp = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex + 1], KEY_NOTIFY | GetRegKeyBitness(pRequest), &hk); - ReleaseRegKey(hk); - fRedo = SUCCEEDED(hrTemp); - break; - default: - Assert(false); - } - } - } while (fRedo); - - MonExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]); - - if (MON_DIRECTORY == pRequest->type) - { - dev.dbch_size = sizeof(dev); - dev.dbch_devicetype = DBT_DEVTYP_HANDLE; - dev.dbch_handle = *pHandle; - // Ignore failure on this - some drives by design don't support it (like network paths), and the worst that can happen is a - // removable device will be left in use so user cannot gracefully remove - pRequest->hNotify = RegisterDeviceNotification(pRequest->hwnd, &dev, DEVICE_NOTIFY_WINDOW_HANDLE); - } - -LExit: - ReleaseRegKey(hk); - - return hr; -} - -static DWORD WINAPI WaiterThread( - __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - HRESULT hrTemp = S_OK; - DWORD dwRet = 0; - BOOL fAgain = FALSE; - BOOL fContinue = TRUE; - BOOL fNotify = FALSE; - BOOL fRet = FALSE; - MSG msg = { }; - MON_ADD_MESSAGE *pAddMessage = NULL; - MON_REMOVE_MESSAGE *pRemoveMessage = NULL; - MON_WAITER_CONTEXT *pWaiterContext = reinterpret_cast(pvContext); - DWORD dwRequestIndex; - DWORD dwNewRequestIndex; - // 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) - DWORD dwWait = 0; - DWORD uCurrentTime = 0; - DWORD uLastTimeInMs = ::GetTickCount(); - DWORD uDeltaInMs = 0; - DWORD cRequestsPendingBeforeLoop = 0; - LPWSTR sczDirectory = NULL; - bool rgfProcessedIndex[MON_MAX_MONITORS_PER_THREAD + 1] = { }; - MON_INTERNAL_TEMPORARY_WAIT * pInternalWait = NULL; - - // Ensure the thread has a message queue - ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - pWaiterContext->fWaiterThreadMessageQueueInitialized = TRUE; - - do - { - dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE); - - uCurrentTime = ::GetTickCount(); - uDeltaInMs = uCurrentTime - uLastTimeInMs; - uLastTimeInMs = uCurrentTime; - - if (WAIT_OBJECT_0 == dwRet) - { - do - { - fRet = ::PeekMessage(&msg, reinterpret_cast(-1), 0, 0, PM_REMOVE); - fAgain = fRet; - if (fRet) - { - switch (msg.message) - { - case MON_MESSAGE_ADD: - pAddMessage = reinterpret_cast(msg.wParam); - - // Don't just blindly put it at the end of the array - it must be before any failing requests - // for WaitForMultipleObjects() to succeed - dwNewRequestIndex = pWaiterContext->cRequests - pWaiterContext->cRequestsFailing; - if (FAILED(pAddMessage->request.hrStatus)) - { - ++pWaiterContext->cRequestsFailing; - } - - hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH); - MonExitOnFailure(hr, "Failed to insert additional handle"); - ++pWaiterContext->cHandles; - - // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL - if (MON_DIRECTORY == pAddMessage->request.type) - { - pWaiterContext->rgHandles[dwNewRequestIndex + 1] = INVALID_HANDLE_VALUE; - } - - hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH); - MonExitOnFailure(hr, "Failed to insert additional request struct"); - ++pWaiterContext->cRequests; - - pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request; - pWaiterContext->rgHandles[dwNewRequestIndex + 1] = pAddMessage->handle; - - ReleaseNullMem(pAddMessage); - break; - - case MON_MESSAGE_REMOVE: - pRemoveMessage = reinterpret_cast(msg.wParam); - - // Find the request to remove - hr = FindRequestIndex(pWaiterContext, pRemoveMessage, &dwRequestIndex); - if (E_NOTFOUND == hr) - { - // Coordinator sends removes blindly to all waiter threads, so maybe this one wasn't intended for us - hr = S_OK; - } - else - { - MonExitOnFailure(hr, "Failed to find request index for remove message"); - - hr = RemoveRequest(pWaiterContext, dwRequestIndex); - MonExitOnFailure(hr, "Failed to remove request after request from coordinator thread."); - } - - MonRemoveMessageDestroy(pRemoveMessage); - pRemoveMessage = NULL; - break; - - case MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS: - if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, PM_NOREMOVE)) - { - // If there is another a pending retry failed wait message, skip this one - continue; - } - - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && FAILED(pWaiterContext->rgRequests[i].hrStatus)) - { - // This is not a failure, just record this in the request's status - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS: - if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, PM_NOREMOVE)) - { - // If there is another a pending retry successful wait message, skip this one - continue; - } - - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && SUCCEEDED(pWaiterContext->rgRequests[i].hrStatus)) - { - // This is not a failure, just record this in the request's status - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_NETWORK_STATUS_UPDATE: - if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_STATUS_UPDATE, MON_MESSAGE_NETWORK_STATUS_UPDATE, PM_NOREMOVE)) - { - // If there is another a pending network status update message, skip this one - continue; - } - - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork) - { - // Failures here get recorded in the request's status - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_DRIVE_STATUS_UPDATE: - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].sczOriginalPathRequest[0] == static_cast(msg.wParam)) - { - // Failures here get recorded in the request's status - if (static_cast(msg.lParam)) - { - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - } - else - { - // If the message says the drive is disconnected, don't even try to wait, just mark it as gone - hrTemp = E_PATHNOTFOUND; - } - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_DRIVE_QUERY_REMOVE: - pInternalWait = reinterpret_cast(msg.wParam); - // Only do any work if message is not yet out of date - // While it could become out of date while doing this processing, sending thread will check response to guard against this - if (pInternalWait->dwSendIteration == static_cast(msg.lParam)) - { - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgHandles[i + 1] == reinterpret_cast(pInternalWait->pvContext)) - { - // Release handles ASAP so the remove request will succeed - if (pWaiterContext->rgRequests[i].hNotify) - { - UnregisterDeviceNotification(pWaiterContext->rgRequests[i].hNotify); - pWaiterContext->rgRequests[i].hNotify = NULL; - } - ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); - pWaiterContext->rgHandles[i + 1] = INVALID_HANDLE_VALUE; - - // Reply to unblock our reply to the remove request - pInternalWait->dwReceiveIteration = static_cast(msg.lParam); - if (!::SetEvent(pInternalWait->hWait)) - { - 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"); - } - - // Drive is disconnecting, don't even try to wait, just mark it as gone - hrTemp = E_PATHNOTFOUND; - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - break; - } - } - } - break; - - case MON_MESSAGE_STOP: - // Stop requested, so abort the whole thread - Trace(REPORT_DEBUG, "Waiter thread was told to stop"); - fAgain = FALSE; - fContinue = FALSE; - ExitFunction1(hr = static_cast(msg.wParam)); - - default: - Assert(false); - break; - } - } - } while (fAgain); - } - else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles) - { - // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist - dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1; - fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1); - - // Initiate re-waits before we notify callback, to ensure we don't miss a single update - hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1); - hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - // 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 - if (SUCCEEDED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus) && (fNotify || (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1))) - { - Trace(REPORT_DEBUG, "Changes detected, waiting for silence period index %u", dwRequestIndex); - - if (0 < pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) - { - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs = 0; - pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = TRUE; - - if (!pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) - { - pWaiterContext->rgRequests[dwRequestIndex].fPendingFire = TRUE; - ++pWaiterContext->cRequestsPending; - } - } - else - { - // If no silence period, notify immediately - Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); - } - } - } - else if (WAIT_TIMEOUT != dwRet) - { - MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); - } - - // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire - // 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) - if (0 < pWaiterContext->cRequestsPending) - { - // Start at max value and find the lowest wait we can below that - dwWait = DWORD_MAX; - cRequestsPendingBeforeLoop = pWaiterContext->cRequestsPending; - - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (pWaiterContext->rgRequests[i].fPendingFire) - { - if (0 == cRequestsPendingBeforeLoop) - { - Assert(FALSE); - hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); - MonExitOnFailure(hr, "Phantom pending fires were found!"); - } - --cRequestsPendingBeforeLoop; - - dwRequestIndex = i; - - if (pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd) - { - pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = FALSE; - } - else - { - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs += uDeltaInMs; - } - - // silence period has elapsed without further notifications, so reset pending-related variables, and finally fire a notify! - if (pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs >= pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) - { - Trace(REPORT_DEBUG, "Silence period surpassed, notifying %u ms late", pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs); - Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); - } - else - { - // set dwWait to the shortest interval period so that if no changes occur, WaitForMultipleObjects - // wakes the thread back up when it's time to fire the next pending notification - if (dwWait > pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs) - { - dwWait = pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs; - } - } - } - } - - // Some post-loop list validation for sanity checking - if (0 < cRequestsPendingBeforeLoop) - { - Assert(FALSE); - hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA); - MonExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait); - } - if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait) - { - Assert(FALSE); - hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT); - MonExitOnFailure(hr, "Pending fires exist (%u), but wait was infinite", cRequestsPendingBeforeLoop); - } - } - } while (fContinue); - - // Don't bother firing pending notifications. We were told to stop monitoring, so client doesn't care. - -LExit: - ReleaseStr(sczDirectory); - MonAddMessageDestroy(pAddMessage); - MonRemoveMessageDestroy(pRemoveMessage); - - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - MonRequestDestroy(pWaiterContext->rgRequests + i); - - switch (pWaiterContext->rgRequests[i].type) - { - case MON_DIRECTORY: - if (INVALID_HANDLE_VALUE != pWaiterContext->rgHandles[i + 1]) - { - ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); - } - break; - case MON_REGKEY: - ReleaseHandle(pWaiterContext->rgHandles[i + 1]); - break; - default: - Assert(false); - } - } - - if (FAILED(hr)) - { - // If waiter thread fails, notify general callback of an error - Assert(pWaiterContext->vpfMonGeneral); - pWaiterContext->vpfMonGeneral(hr, pWaiterContext->pvContext); - - // And tell coordinator to shut all other waiters down - if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) - { - TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to coordinator thread to stop (due to general failure)."); - } - } - - return hr; -} - -static void Notify( - __in HRESULT hr, - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REQUEST *pRequest - ) -{ - if (pRequest->fPendingFire) - { - --pWaiterContext->cRequestsPending; - } - - pRequest->fPendingFire = FALSE; - pRequest->fSkipDeltaAdd = FALSE; - pRequest->dwSilencePeriodInMs = 0; - - switch (pRequest->type) - { - case MON_DIRECTORY: - Assert(pWaiterContext->vpfMonDirectory); - pWaiterContext->vpfMonDirectory(hr, pRequest->sczOriginalPathRequest, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); - break; - case MON_REGKEY: - Assert(pWaiterContext->vpfMonRegKey); - pWaiterContext->vpfMonRegKey(hr, pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1], pRequest->regkey.kbKeyBitness, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); - break; - default: - Assert(false); - } -} - -static BOOL GetRecursiveFlag( - __in MON_REQUEST *pRequest, - __in DWORD dwIndex - ) -{ - if (pRequest->cPathHierarchy - 1 == dwIndex) - { - return pRequest->fRecursive; - } - else - { - return FALSE; - } -} - -static HRESULT FindRequestIndex( - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REMOVE_MESSAGE *pMessage, - __out DWORD *pdwIndex - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (pWaiterContext->rgRequests[i].type == pMessage->type) - { - switch (pWaiterContext->rgRequests[i].type) - { - case MON_DIRECTORY: - 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) - { - *pdwIndex = i; - ExitFunction1(hr = S_OK); - } - break; - case MON_REGKEY: - if (reinterpret_cast(pMessage->regkey.hkRoot) == reinterpret_cast(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) - { - *pdwIndex = i; - ExitFunction1(hr = S_OK); - } - break; - default: - Assert(false); - } - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -static HRESULT RemoveRequest( - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex - ) -{ - HRESULT hr = S_OK; - - MonRequestDestroy(pWaiterContext->rgRequests + dwRequestIndex); - - switch (pWaiterContext->rgRequests[dwRequestIndex].type) - { - case MON_DIRECTORY: - if (pWaiterContext->rgHandles[dwRequestIndex + 1] != INVALID_HANDLE_VALUE) - { - ::FindCloseChangeNotification(pWaiterContext->rgHandles[dwRequestIndex + 1]); - } - break; - case MON_REGKEY: - ReleaseHandle(pWaiterContext->rgHandles[dwRequestIndex + 1]); - break; - default: - Assert(false); - } - - if (pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) - { - --pWaiterContext->cRequestsPending; - } - - if (FAILED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus)) - { - --pWaiterContext->cRequestsFailing; - } - - MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), TRUE); - --pWaiterContext->cHandles; - MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), TRUE); - --pWaiterContext->cRequests; - - // Notify coordinator thread that a wait was removed - if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast(::GetCurrentThreadId()), 0)) - { - MonExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed."); - } - -LExit: - return hr; -} - -static REGSAM GetRegKeyBitness( - __in MON_REQUEST *pRequest - ) -{ - if (REG_KEY_32BIT == pRequest->regkey.kbKeyBitness) - { - return KEY_WOW64_32KEY; - } - else if (REG_KEY_64BIT == pRequest->regkey.kbKeyBitness) - { - return KEY_WOW64_64KEY; - } - else - { - return 0; - } -} - -static HRESULT DuplicateRemoveMessage( - __in MON_REMOVE_MESSAGE *pMessage, - __out MON_REMOVE_MESSAGE **ppMessage - ) -{ - HRESULT hr = S_OK; - - *ppMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - MonExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message"); - - (*ppMessage)->type = pMessage->type; - (*ppMessage)->fRecursive = pMessage->fRecursive; - - switch (pMessage->type) - { - case MON_DIRECTORY: - hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0); - MonExitOnFailure(hr, "Failed to copy directory"); - break; - case MON_REGKEY: - (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot; - (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness; - hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0); - MonExitOnFailure(hr, "Failed to copy subkey"); - break; - default: - Assert(false); - break; - } - -LExit: - return hr; -} - -static LRESULT CALLBACK MonWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - HRESULT hr = S_OK; - DEV_BROADCAST_HDR *pHdr = NULL; - DEV_BROADCAST_HANDLE *pHandle = NULL; - DEV_BROADCAST_VOLUME *pVolume = NULL; - DWORD dwUnitMask = 0; - DWORD er = ERROR_SUCCESS; - WCHAR chDrive = L'\0'; - BOOL fArrival = FALSE; - BOOL fReturnTrue = FALSE; - CREATESTRUCT *pCreateStruct = NULL; - MON_WAITER_CONTEXT *pWaiterContext = NULL; - MON_STRUCT *pm = NULL; - - // keep track of the MON_STRUCT pointer that was passed in on init, associate it with the window - if (WM_CREATE == uMsg) - { - pCreateStruct = reinterpret_cast(lParam); - if (pCreateStruct) - { - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); - } - } - else if (WM_NCDESTROY == uMsg) - { - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - } - - // Note this message ONLY comes in through WndProc, it isn't visible from the GetMessage loop. - else if (WM_DEVICECHANGE == uMsg) - { - if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam) - { - fArrival = DBT_DEVICEARRIVAL == wParam; - - pHdr = reinterpret_cast(lParam); - if (DBT_DEVTYP_VOLUME == pHdr->dbch_devicetype) - { - pVolume = reinterpret_cast(lParam); - dwUnitMask = pVolume->dbcv_unitmask; - chDrive = L'a'; - while (0 < dwUnitMask) - { - if (dwUnitMask & 0x1) - { - // This drive had a status update, so send it out to all threads - if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast(chDrive), static_cast(fArrival))) - { - MonExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE"); - } - } - dwUnitMask >>= 1; - ++chDrive; - - if (chDrive == 'z') - { - hr = E_UNEXPECTED; - MonExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask); - } - } - } - } - // We can only process device query remove messages if we have a MON_STRUCT pointer - else if (DBT_DEVICEQUERYREMOVE == wParam) - { - pm = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); - if (!pm) - { - hr = E_POINTER; - MonExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored"); - } - - fReturnTrue = TRUE; - - pHdr = reinterpret_cast(lParam); - if (DBT_DEVTYP_HANDLE == pHdr->dbch_devicetype) - { - // 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 - // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread - pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL); - MonExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed"); - - pHandle = reinterpret_cast(lParam); - pm->internalWait.pvContext = pHandle->dbch_handle; - pm->internalWait.dwReceiveIteration = pm->internalWait.dwSendIteration - 1; - // This drive had a status update, so send it out to all threads - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast(&pm->internalWait), static_cast(pm->internalWait.dwSendIteration))) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message"); - } - } - - er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); - // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response - if (WAIT_OBJECT_0 == er) - { - // If the response ID matches what we sent, we actually got a valid reply! - if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration) - { - TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply"); - } - } - else if (WAIT_TIMEOUT == er) - { - TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message"); - } - else - { - MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); - } - ++pm->internalWait.dwSendIteration; - } - } - } - -LExit: - if (pm) - { - ReleaseHandle(pm->internalWait.hWait); - } - - if (fReturnTrue) - { - return TRUE; - } - else - { - return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); - } -} - -static HRESULT CreateMonWindow( - __in MON_STRUCT *pm, - __out HWND *pHwnd - ) -{ - HRESULT hr = S_OK; - WNDCLASSW wc = { }; - - wc.lpfnWndProc = MonWndProc; - wc.hInstance = ::GetModuleHandleW(NULL); - wc.lpszClassName = MONUTIL_WINDOW_CLASS; - if (!::RegisterClassW(&wc)) - { - if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError()) - { - MonExitWithLastError(hr, "Failed to register MonUtil window class."); - } - } - - *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm); - MonExitOnNullWithLastError(*pHwnd, hr, "Failed to create window."); - - // 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 - // SWP_NOACTIVATE is important so the currently active window doesn't lose focus - SetWindowPos(*pHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_DEFERERASE | SWP_NOACTIVATE); - -LExit: - return hr; -} - -static HRESULT WaitForNetworkChanges( - __inout HANDLE *phMonitor, - __in MON_STRUCT *pm - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - DWORD dwBytesReturned = 0; - WSACOMPLETION wsaCompletion = { }; - WSAQUERYSET qsRestrictions = { }; - - qsRestrictions.dwSize = sizeof(WSAQUERYSET); - qsRestrictions.dwNameSpace = NS_NLA; - - if (NULL != *phMonitor) - { - ::WSALookupServiceEnd(*phMonitor); - *phMonitor = NULL; - } - - if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor)) - { - hr = HRESULT_FROM_WIN32(::WSAGetLastError()); - MonExitOnFailure(hr, "WSALookupServiceBegin() failed"); - } - - wsaCompletion.Type = NSP_NOTIFY_HWND; - wsaCompletion.Parameters.WindowMessage.hWnd = pm->hwnd; - wsaCompletion.Parameters.WindowMessage.uMsg = MON_MESSAGE_NETWORK_STATUS_UPDATE; - nResult = ::WSANSPIoctl(*phMonitor, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &dwBytesReturned, &wsaCompletion); - if (SOCKET_ERROR != nResult || WSA_IO_PENDING != ::WSAGetLastError()) - { - hr = HRESULT_FROM_WIN32(::WSAGetLastError()); - if (SUCCEEDED(hr)) - { - hr = E_FAIL; - } - MonExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError()); - } - -LExit: - return hr; -} - -static HRESULT UpdateWaitStatus( - __in HRESULT hrNewStatus, - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex, - __out_opt DWORD *pdwNewRequestIndex - ) -{ - HRESULT hr = S_OK; - DWORD dwNewRequestIndex; - MON_REQUEST *pRequest = pWaiterContext->rgRequests + dwRequestIndex; - - if (NULL != pdwNewRequestIndex) - { - *pdwNewRequestIndex = dwRequestIndex; - } - - if (SUCCEEDED(pRequest->hrStatus) || SUCCEEDED(hrNewStatus)) - { - // If it's a network wait, notify as long as it's new status is successful because we *may* have lost some changes - // before the wait was re-initiated. Otherwise, only notify if there was an interesting status change - if (SUCCEEDED(pRequest->hrStatus) != SUCCEEDED(hrNewStatus) || (pRequest->fNetwork && SUCCEEDED(hrNewStatus))) - { - Notify(hrNewStatus, pWaiterContext, pRequest); - } - - if (SUCCEEDED(pRequest->hrStatus) && FAILED(hrNewStatus)) - { - // If it's a network wait, notify coordinator thread that a network wait is failing - if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail"); - } - - // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle - ++pWaiterContext->cRequestsFailing; - dwNewRequestIndex = pWaiterContext->cRequests - 1; - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); - // Reset pRequest to the newly swapped item - pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; - if (NULL != pdwNewRequestIndex) - { - *pdwNewRequestIndex = dwNewRequestIndex; - } - } - else if (FAILED(pRequest->hrStatus) && SUCCEEDED(hrNewStatus)) - { - Assert(pWaiterContext->cRequestsFailing > 0); - // If it's a network wait, notify coordinator thread that a network wait is succeeding again - if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again"); - } - - --pWaiterContext->cRequestsFailing; - dwNewRequestIndex = 0; - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); - // Reset pRequest to the newly swapped item - pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; - if (NULL != pdwNewRequestIndex) - { - *pdwNewRequestIndex = dwNewRequestIndex; - } - } - } - - pRequest->hrStatus = hrNewStatus; - -LExit: - return hr; -} diff --git a/src/dutil/osutil.cpp b/src/dutil/osutil.cpp deleted file mode 100644 index 880ec3ea..00000000 --- a/src/dutil/osutil.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define OsExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) -#define OsExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) -#define OsExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) -#define OsExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) -#define OsExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_OSUTIL, e, x, s, __VA_ARGS__) -#define OsExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_OSUTIL, g, x, s, __VA_ARGS__) - -typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); - -OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; -DWORD vdwOsServicePack = 0; -RTL_OSVERSIONINFOEXW vovix = { }; - -/******************************************************************** - OsGetVersion - -********************************************************************/ -extern "C" void DAPI OsGetVersion( - __out OS_VERSION* pVersion, - __out DWORD* pdwServicePack - ) -{ - OSVERSIONINFOEXW ovi = { }; - - if (OS_VERSION_UNKNOWN == vOsVersion) - { - ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); - -#pragma warning (push) -#pragma warning(suppress: 4996) // deprecated -#pragma warning (push) -#pragma warning(suppress: 28159)// deprecated, use other function instead - ::GetVersionExW(reinterpret_cast(&ovi)); // only fails if version info size is set incorrectly. -#pragma warning (pop) -#pragma warning (pop) - - vdwOsServicePack = static_cast(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor; - if (4 == ovi.dwMajorVersion) - { - vOsVersion = OS_VERSION_WINNT; - } - else if (5 == ovi.dwMajorVersion) - { - if (0 == ovi.dwMinorVersion) - { - vOsVersion = OS_VERSION_WIN2000; - } - else if (1 == ovi.dwMinorVersion) - { - vOsVersion = OS_VERSION_WINXP; - } - else if (2 == ovi.dwMinorVersion) - { - vOsVersion = OS_VERSION_WIN2003; - } - else - { - vOsVersion = OS_VERSION_FUTURE; - } - } - else if (6 == ovi.dwMajorVersion) - { - if (0 == ovi.dwMinorVersion) - { - vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008; - } - else if (1 == ovi.dwMinorVersion) - { - vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2; - } - else - { - vOsVersion = OS_VERSION_FUTURE; - } - } - else - { - vOsVersion = OS_VERSION_FUTURE; - } - } - - *pVersion = vOsVersion; - *pdwServicePack = vdwOsServicePack; -} - -extern "C" HRESULT DAPI OsCouldRunPrivileged( - __out BOOL* pfPrivileged - ) -{ - HRESULT hr = S_OK; - BOOL fUacEnabled = FALSE; - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - PSID AdministratorsGroup = NULL; - - // Do a best effort check to see if UAC is enabled on this machine. - OsIsUacEnabled(&fUacEnabled); - - // If UAC is enabled then the process could run privileged by asking to elevate. - if (fUacEnabled) - { - *pfPrivileged = TRUE; - } - else // no UAC so only privilged if user is in administrators group. - { - *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); - if (*pfPrivileged) - { - if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) - { - *pfPrivileged = FALSE; - } - } - } - - ReleaseSid(AdministratorsGroup); - return hr; -} - -extern "C" HRESULT DAPI OsIsRunningPrivileged( - __out BOOL* pfPrivileged - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - HANDLE hToken = NULL; - TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault; - DWORD dwSize = 0; - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - PSID AdministratorsGroup = NULL; - - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) - { - OsExitOnLastError(hr, "Failed to open process token."); - } - - if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) - { - *pfPrivileged = (TokenElevationTypeFull == elevationType); - ExitFunction1(hr = S_OK); - } - - // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check - er = ::GetLastError(); - if (ERROR_INVALID_FUNCTION == er) - { - er = ERROR_SUCCESS; - } - OsExitOnWin32Error(er, hr, "Failed to get process token information."); - - // Fallback to this check for some OS's (like XP) - *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); - if (*pfPrivileged) - { - if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) - { - *pfPrivileged = FALSE; - } - } - -LExit: - ReleaseSid(AdministratorsGroup); - - if (hToken) - { - ::CloseHandle(hToken); - } - - return hr; -} - -extern "C" HRESULT DAPI OsIsUacEnabled( - __out BOOL* pfUacEnabled - ) -{ - HRESULT hr = S_OK; - HKEY hk = NULL; - DWORD dwUacEnabled = 0; - - *pfUacEnabled = FALSE; // assume UAC not enabled. - - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - OsExitOnFailure(hr, "Failed to open system policy key to detect UAC."); - - hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - OsExitOnFailure(hr, "Failed to read registry value to detect UAC."); - - *pfUacEnabled = (0 != dwUacEnabled); - -LExit: - ReleaseRegKey(hk); - - return hr; -} - -HRESULT DAPI OsRtlGetVersion( - __inout RTL_OSVERSIONINFOEXW* pOvix - ) -{ - HRESULT hr = S_OK; - HMODULE hNtdll = NULL; - PFN_RTL_GET_VERSION pfnRtlGetVersion = NULL; - - if (vovix.dwOSVersionInfoSize) - { - ExitFunction(); - } - - vovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); - - hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll); - if (E_MODNOTFOUND == hr) - { - OsExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); - } - OsExitOnFailure(hr, "Failed to load ntdll.dll."); - - pfnRtlGetVersion = reinterpret_cast(::GetProcAddress(hNtdll, "RtlGetVersion")); - OsExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); - - hr = static_cast(pfnRtlGetVersion(&vovix)); - -LExit: - memcpy(pOvix, &vovix, sizeof(RTL_OSVERSIONINFOEXW)); - - if (hNtdll) - { - ::FreeLibrary(hNtdll); - } - - return hr; -} diff --git a/src/dutil/packages.config b/src/dutil/packages.config deleted file mode 100644 index 5bbcd994..00000000 --- a/src/dutil/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/dutil/path2utl.cpp b/src/dutil/path2utl.cpp deleted file mode 100644 index ff3a946d..00000000 --- a/src/dutil/path2utl.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) -#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) - - -DAPI_(HRESULT) PathCanonicalizePath( - __in_z LPCWSTR wzPath, - __deref_out_z LPWSTR* psczCanonicalized - ) -{ - HRESULT hr = S_OK; - int cch = MAX_PATH + 1; - - hr = StrAlloc(psczCanonicalized, cch); - PathExitOnFailure(hr, "Failed to allocate string for the canonicalized path."); - - if (::PathCanonicalizeW(*psczCanonicalized, wzPath)) - { - hr = S_OK; - } - else - { - ExitFunctionWithLastError(hr); - } - -LExit: - return hr; -} - -DAPI_(HRESULT) PathDirectoryContainsPath( - __in_z LPCWSTR wzDirectory, - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - LPWSTR sczDirectory = NULL; - LPWSTR sczOriginalPath = NULL; - LPWSTR sczOriginalDirectory = NULL; - - hr = PathCanonicalizePath(wzPath, &sczOriginalPath); - PathExitOnFailure(hr, "Failed to canonicalize the path."); - - hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory); - PathExitOnFailure(hr, "Failed to canonicalize the directory."); - - if (!sczOriginalPath || !*sczOriginalPath) - { - ExitFunction1(hr = S_FALSE); - } - if (!sczOriginalDirectory || !*sczOriginalDirectory) - { - ExitFunction1(hr = S_FALSE); - } - - sczPath = sczOriginalPath; - sczDirectory = sczOriginalDirectory; - - for (; *sczDirectory;) - { - if (!*sczPath) - { - ExitFunction1(hr = S_FALSE); - } - - if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1)) - { - ExitFunction1(hr = S_FALSE); - } - - ++sczDirectory; - ++sczPath; - } - - --sczDirectory; - if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath) - { - hr = S_OK; - } - else - { - hr = S_FALSE; - } - -LExit: - ReleaseStr(sczOriginalPath); - ReleaseStr(sczOriginalDirectory); - return hr; -} diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp deleted file mode 100644 index 7c3cfe06..00000000 --- a/src/dutil/pathutil.cpp +++ /dev/null @@ -1,1083 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) -#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) - -#define PATH_GOOD_ENOUGH 64 - - -DAPI_(HRESULT) PathCommandLineAppend( - __deref_inout_z LPWSTR* psczCommandLine, - __in_z LPCWSTR wzArgument - ) -{ - HRESULT hr = S_OK; - LPWSTR sczQuotedArg = NULL; - BOOL fRequiresQuoting = FALSE; - DWORD dwMaxEscapedSize = 0; - - // Loop through the argument determining if it needs to be quoted and what the maximum - // size would be if there are escape characters required. - for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) - { - // Arguments with whitespace need quoting. - if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) - { - fRequiresQuoting = TRUE; - } - else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. - { - fRequiresQuoting = TRUE; - ++dwMaxEscapedSize; - } - else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. - { - ++dwMaxEscapedSize; - } - - ++dwMaxEscapedSize; - } - - // If we found anything in the argument that requires our argument to be quoted - if (fRequiresQuoting) - { - hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. - PathExitOnFailure(hr, "Failed to allocate argument to be quoted."); - - LPCWSTR pwz = wzArgument; - LPWSTR pwzQuoted = sczQuotedArg; - - *pwzQuoted = L'"'; - ++pwzQuoted; - while (*pwz) - { - DWORD dwBackslashes = 0; - while (L'\\' == *pwz) - { - ++dwBackslashes; - ++pwz; - } - - // Escape all backslashes at the end of the string. - if (!*pwz) - { - dwBackslashes *= 2; - } - else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. - { - dwBackslashes = dwBackslashes * 2 + 1; - } - // the backslashes don't have to be escaped. - - // Add the appropriate number of backslashes - for (DWORD i = 0; i < dwBackslashes; ++i) - { - *pwzQuoted = L'\\'; - ++pwzQuoted; - } - - // If there is a character, add it after all the escaped backslashes - if (*pwz) - { - *pwzQuoted = *pwz; - ++pwz; - ++pwzQuoted; - } - } - - *pwzQuoted = L'"'; - ++pwzQuoted; - *pwzQuoted = L'\0'; // ensure the arg is null terminated. - } - - // If there is already data in the command line, append a space before appending the - // argument. - if (*psczCommandLine && **psczCommandLine) - { - hr = StrAllocConcat(psczCommandLine, L" ", 0); - PathExitOnFailure(hr, "Failed to append space to command line with existing data."); - } - - hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); - PathExitOnFailure(hr, "Failed to copy command line argument."); - -LExit: - ReleaseStr(sczQuotedArg); - - return hr; -} - - -DAPI_(LPWSTR) PathFile( - __in_z LPCWSTR wzPath - ) -{ - if (!wzPath) - { - return NULL; - } - - LPWSTR wzFile = const_cast(wzPath); - for (LPWSTR wz = wzFile; *wz; ++wz) - { - // valid delineators - // \ => Windows path - // / => unix and URL path - // : => relative path from mapped root - if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) - { - wzFile = wz + 1; - } - } - - return wzFile; -} - - -DAPI_(LPCWSTR) PathExtension( - __in_z LPCWSTR wzPath - ) -{ - if (!wzPath) - { - return NULL; - } - - // Find the last dot in the last thing that could be a file. - LPCWSTR wzExtension = NULL; - for (LPCWSTR wz = wzPath; *wz; ++wz) - { - if (L'\\' == *wz || L'/' == *wz || L':' == *wz) - { - wzExtension = NULL; - } - else if (L'.' == *wz) - { - wzExtension = wz; - } - } - - return wzExtension; -} - - -DAPI_(HRESULT) PathGetDirectory( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczDirectory - ) -{ - HRESULT hr = S_OK; - size_t cchDirectory = SIZE_T_MAX; - - for (LPCWSTR wz = wzPath; *wz; ++wz) - { - // valid delineators: - // \ => Windows path - // / => unix and URL path - // : => relative path from mapped root - if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) - { - cchDirectory = static_cast(wz - wzPath) + 1; - } - } - - if (SIZE_T_MAX == cchDirectory) - { - // we were given just a file name, so there's no directory available - return S_FALSE; - } - - if (wzPath[0] == L'\"') - { - ++wzPath; - --cchDirectory; - } - - hr = StrAllocString(psczDirectory, wzPath, cchDirectory); - PathExitOnFailure(hr, "Failed to copy directory."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathGetParentPath( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczParent - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzParent = NULL; - - for (LPCWSTR wz = wzPath; *wz; ++wz) - { - if (wz[1] && (L'\\' == *wz || L'/' == *wz)) - { - wzParent = wz; - } - } - - if (wzParent) - { - size_t cchPath = static_cast(wzParent - wzPath) + 1; - - hr = StrAllocString(psczParent, wzPath, cchPath); - PathExitOnFailure(hr, "Failed to copy directory."); - } - else - { - ReleaseNullStr(psczParent); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathExpand( - __out LPWSTR *psczFullPath, - __in_z LPCWSTR wzRelativePath, - __in DWORD dwResolveFlags - ) -{ - Assert(wzRelativePath && *wzRelativePath); - - HRESULT hr = S_OK; - DWORD cch = 0; - LPWSTR sczExpandedPath = NULL; - DWORD cchExpandedPath = 0; - SIZE_T cbSize = 0; - - LPWSTR sczFullPath = NULL; - - // - // First, expand any environment variables. - // - if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT) - { - cchExpandedPath = PATH_GOOD_ENOUGH; - - hr = StrAlloc(&sczExpandedPath, cchExpandedPath); - PathExitOnFailure(hr, "Failed to allocate space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - cchExpandedPath = cch; - hr = StrAlloc(&sczExpandedPath, cchExpandedPath); - PathExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - PathExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); - } - } - - if (MAX_PATH < cch) - { - hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet - if (E_INVALIDARG == hr) - { - hr = S_OK; - } - PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); - - hr = StrMaxLength(sczExpandedPath, &cbSize); - PathExitOnFailure(hr, "Failed to get max length of expanded path."); - - cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize); - } - } - - // - // Second, get the full path. - // - if (dwResolveFlags & PATH_EXPAND_FULLPATH) - { - LPWSTR wzFileName = NULL; - LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; - DWORD cchFullPath = max(PATH_GOOD_ENOUGH, cchExpandedPath); - - hr = StrAlloc(&sczFullPath, cchFullPath); - PathExitOnFailure(hr, "Failed to allocate space for full path."); - - cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); - } - else if (cchFullPath < cch) - { - cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed - hr = StrAlloc(&sczFullPath, cchFullPath); - PathExitOnFailure(hr, "Failed to re-allocate more space for full path."); - - cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); - } - else if (cchFullPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - PathExitOnRootFailure(hr, "Failed to allocate buffer for full path."); - } - } - - if (MAX_PATH < cch) - { - hr = PathPrefix(&sczFullPath); - PathExitOnFailure(hr, "Failed to prefix long path after expanding."); - } - } - else - { - sczFullPath = sczExpandedPath; - sczExpandedPath = NULL; - } - - hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); - PathExitOnFailure(hr, "Failed to copy relative path into full path."); - -LExit: - ReleaseStr(sczFullPath); - ReleaseStr(sczExpandedPath); - - return hr; -} - - -DAPI_(HRESULT) PathPrefix( - __inout LPWSTR *psczFullPath - ) -{ - Assert(psczFullPath && *psczFullPath); - - HRESULT hr = S_OK; - LPWSTR wzFullPath = *psczFullPath; - SIZE_T cbFullPath = 0; - - if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || - (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && - L':' == wzFullPath[1] && - L'\\' == wzFullPath[2]) // normal path - { - hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); - PathExitOnFailure(hr, "Failed to add prefix to file path."); - } - else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC - { - // ensure that we're not already prefixed - if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) - { - hr = StrSize(*psczFullPath, &cbFullPath); - PathExitOnFailure(hr, "Failed to get size of full path."); - - memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); - - hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); - PathExitOnFailure(hr, "Failed to add prefix to UNC path."); - } - } - else - { - hr = E_INVALIDARG; - PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathFixedBackslashTerminate( - __inout_ecount_z(cchPath) LPWSTR wzPath, - __in SIZE_T cchPath - ) -{ - HRESULT hr = S_OK; - size_t cchLength = 0; - - hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); - PathExitOnFailure(hr, "Failed to get length of path."); - - if (cchLength >= cchPath) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - } - else if (L'\\' != wzPath[cchLength - 1]) - { - wzPath[cchLength] = L'\\'; - wzPath[cchLength + 1] = L'\0'; - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathBackslashTerminate( - __inout LPWSTR* psczPath - ) -{ - Assert(psczPath && *psczPath); - - HRESULT hr = S_OK; - SIZE_T cchPath = 0; - size_t cchLength = 0; - - hr = StrMaxLength(*psczPath, &cchPath); - PathExitOnFailure(hr, "Failed to get size of path string."); - - hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); - PathExitOnFailure(hr, "Failed to get length of path."); - - if (L'\\' != (*psczPath)[cchLength - 1]) - { - hr = StrAllocConcat(psczPath, L"\\", 1); - PathExitOnFailure(hr, "Failed to concat backslash onto string."); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathForCurrentProcess( - __inout LPWSTR *psczFullPath, - __in_opt HMODULE hModule - ) -{ - HRESULT hr = S_OK; - DWORD cch = MAX_PATH; - - do - { - hr = StrAlloc(psczFullPath, cch); - PathExitOnFailure(hr, "Failed to allocate string for module path."); - - DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); - if (0 == cchRequired) - { - PathExitWithLastError(hr, "Failed to get path for executing process."); - } - else if (cchRequired == cch) - { - cch = cchRequired + 1; - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - } - else - { - hr = S_OK; - } - } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathRelativeToModule( - __inout LPWSTR *psczFullPath, - __in_opt LPCWSTR wzFileName, - __in_opt HMODULE hModule - ) -{ - HRESULT hr = PathForCurrentProcess(psczFullPath, hModule); - PathExitOnFailure(hr, "Failed to get current module path."); - - hr = PathGetDirectory(*psczFullPath, psczFullPath); - PathExitOnFailure(hr, "Failed to get current module directory."); - - if (wzFileName) - { - hr = PathConcat(*psczFullPath, wzFileName, psczFullPath); - PathExitOnFailure(hr, "Failed to append filename."); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathCreateTempFile( - __in_opt LPCWSTR wzDirectory, - __in_opt __format_string LPCWSTR wzFileNameTemplate, - __in DWORD dwUniqueCount, - __in DWORD dwFileAttributes, - __out_opt LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ) -{ - AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); - - HRESULT hr = S_OK; - - LPWSTR sczTempPath = NULL; - DWORD cchTempPath = MAX_PATH; - - HANDLE hTempFile = INVALID_HANDLE_VALUE; - LPWSTR scz = NULL; - LPWSTR sczTempFile = NULL; - - if (wzDirectory && *wzDirectory) - { - hr = StrAllocString(&sczTempPath, wzDirectory, 0); - PathExitOnFailure(hr, "Failed to copy temp path."); - } - else - { - hr = StrAlloc(&sczTempPath, cchTempPath); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); - - if (!::GetTempPathW(cchTempPath, sczTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp path."); - } - } - - if (wzFileNameTemplate && *wzFileNameTemplate) - { - for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i) - { - hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); - PathExitOnFailure(hr, "Failed to allocate memory for file template."); - - hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); - PathExitOnFailure(hr, "Failed to allocate temp file name."); - - hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // if the file already exists, just try again - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) - { - hr = S_OK; - } - PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); - } - } - } - - // If we were not able to or we did not try to create a temp file, ask - // the system to provide us a temp file using its built-in mechanism. - if (INVALID_HANDLE_VALUE == hTempFile) - { - hr = StrAlloc(&sczTempFile, MAX_PATH); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); - - if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) - { - PathExitWithLastError(hr, "Failed to create new temp file name."); - } - - hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - PathExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); - } - } - - // If the caller wanted the temp file name or handle, return them here. - if (psczTempFile) - { - hr = StrAllocString(psczTempFile, sczTempFile, 0); - PathExitOnFailure(hr, "Failed to copy temp file string."); - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - -LExit: - if (INVALID_HANDLE_VALUE != hTempFile) - { - ::CloseHandle(hTempFile); - } - - ReleaseStr(scz); - ReleaseStr(sczTempFile); - ReleaseStr(sczTempPath); - - return hr; -} - - -DAPI_(HRESULT) PathCreateTimeBasedTempFile( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzPrefix, - __in_z_opt LPCWSTR wzPostfix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ) -{ - HRESULT hr = S_OK; - BOOL fRetry = FALSE; - WCHAR wzTempPath[MAX_PATH] = { }; - LPWSTR sczPrefix = NULL; - LPWSTR sczPrefixFolder = NULL; - SYSTEMTIME time = { }; - - LPWSTR sczTempPath = NULL; - HANDLE hTempFile = INVALID_HANDLE_VALUE; - DWORD dwAttempts = 0; - - if (wzDirectory && *wzDirectory) - { - hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix); - PathExitOnFailure(hr, "Failed to combine directory and log prefix."); - } - else - { - if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp folder."); - } - - hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); - PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); - } - - hr = PathGetDirectory(sczPrefix, &sczPrefixFolder); - if (S_OK == hr) - { - hr = DirEnsureExists(sczPrefixFolder, NULL); - PathExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); - } - - if (!wzPostfix) - { - wzPostfix = L""; - } - - do - { - fRetry = FALSE; - ++dwAttempts; - - ::GetLocalTime(&time); - - // Log format: pre YYYY MM dd hh mm ss post ext - 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); - PathExitOnFailure(hr, "failed to allocate memory for the temp path"); - - hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // If the file already exists, just try again. - DWORD er = ::GetLastError(); - if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er) - { - ::Sleep(100); - - if (10 > dwAttempts) - { - er = ERROR_SUCCESS; - fRetry = TRUE; - } - } - - hr = HRESULT_FROM_WIN32(er); - PathExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); - } - } while (fRetry); - - if (psczTempFile) - { - hr = StrAllocString(psczTempFile, sczTempPath, 0); - PathExitOnFailure(hr, "Failed to copy temp path to return."); - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - -LExit: - ReleaseFile(hTempFile); - ReleaseStr(sczTempPath); - ReleaseStr(sczPrefixFolder); - ReleaseStr(sczPrefix); - - return hr; -} - - -DAPI_(HRESULT) PathCreateTempDirectory( - __in_opt LPCWSTR wzDirectory, - __in __format_string LPCWSTR wzDirectoryNameTemplate, - __in DWORD dwUniqueCount, - __out LPWSTR* psczTempDirectory - ) -{ - AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified."); - AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); - - HRESULT hr = S_OK; - - LPWSTR sczTempPath = NULL; - DWORD cchTempPath = MAX_PATH; - - LPWSTR scz = NULL; - - if (wzDirectory && *wzDirectory) - { - hr = StrAllocString(&sczTempPath, wzDirectory, 0); - PathExitOnFailure(hr, "Failed to copy temp path."); - - hr = PathBackslashTerminate(&sczTempPath); - PathExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); - } - else - { - hr = StrAlloc(&sczTempPath, cchTempPath); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); - - if (!::GetTempPathW(cchTempPath, sczTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp path."); - } - } - - for (DWORD i = 1; i <= dwUniqueCount; ++i) - { - hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i); - PathExitOnFailure(hr, "Failed to allocate memory for directory name template."); - - hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz); - PathExitOnFailure(hr, "Failed to allocate temp directory name."); - - if (!::CreateDirectoryW(*psczTempDirectory, NULL)) - { - DWORD er = ::GetLastError(); - if (ERROR_ALREADY_EXISTS == er) - { - hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); - continue; - } - else if (ERROR_PATH_NOT_FOUND == er) - { - hr = DirEnsureExists(*psczTempDirectory, NULL); - break; - } - else - { - hr = HRESULT_FROM_WIN32(er); - break; - } - } - else - { - hr = S_OK; - break; - } - } - PathExitOnFailure(hr, "Failed to create temp directory."); - - hr = PathBackslashTerminate(psczTempDirectory); - PathExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); - -LExit: - ReleaseStr(scz); - ReleaseStr(sczTempPath); - - return hr; -} - - -DAPI_(HRESULT) PathGetKnownFolder( - __in int csidl, - __out LPWSTR* psczKnownFolder - ) -{ - HRESULT hr = S_OK; - - hr = StrAlloc(psczKnownFolder, MAX_PATH); - PathExitOnFailure(hr, "Failed to allocate memory for known folder."); - - hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); - PathExitOnFailure(hr, "Failed to get known folder path."); - - hr = PathBackslashTerminate(psczKnownFolder); - PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); - -LExit: - return hr; -} - - -DAPI_(BOOL) PathIsAbsolute( - __in_z LPCWSTR wzPath - ) -{ - return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); -} - - -DAPI_(HRESULT) PathConcat( - __in_opt LPCWSTR wzPath1, - __in_opt LPCWSTR wzPath2, - __deref_out_z LPWSTR* psczCombined - ) -{ - return PathConcatCch(wzPath1, 0, wzPath2, 0, psczCombined); -} - - -DAPI_(HRESULT) PathConcatCch( - __in_opt LPCWSTR wzPath1, - __in SIZE_T cchPath1, - __in_opt LPCWSTR wzPath2, - __in SIZE_T cchPath2, - __deref_out_z LPWSTR* psczCombined - ) -{ - HRESULT hr = S_OK; - - if (!wzPath2 || !*wzPath2) - { - hr = StrAllocString(psczCombined, wzPath1, cchPath1); - PathExitOnFailure(hr, "Failed to copy just path1 to output."); - } - else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) - { - hr = StrAllocString(psczCombined, wzPath2, cchPath2); - PathExitOnFailure(hr, "Failed to copy just path2 to output."); - } - else - { - hr = StrAllocString(psczCombined, wzPath1, cchPath1); - PathExitOnFailure(hr, "Failed to copy path1 to output."); - - hr = PathBackslashTerminate(psczCombined); - PathExitOnFailure(hr, "Failed to backslashify."); - - hr = StrAllocConcat(psczCombined, wzPath2, cchPath2); - PathExitOnFailure(hr, "Failed to append path2 to output."); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathEnsureQuoted( - __inout LPWSTR* ppszPath, - __in BOOL fDirectory - ) -{ - Assert(ppszPath && *ppszPath); - - HRESULT hr = S_OK; - size_t cchPath = 0; - - hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath); - PathExitOnFailure(hr, "Failed to get the length of the path."); - - // Handle simple special cases. - if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0])) - { - hr = StrAllocString(ppszPath, L"\"\"", 2); - PathExitOnFailure(hr, "Failed to allocate a quoted empty string."); - - ExitFunction(); - } - - if (L'"' != (*ppszPath)[0]) - { - hr = StrAllocPrefix(ppszPath, L"\"", 1); - PathExitOnFailure(hr, "Failed to allocate an opening quote."); - - // Add a char for the opening quote. - ++cchPath; - } - - if (L'"' != (*ppszPath)[cchPath - 1]) - { - hr = StrAllocConcat(ppszPath, L"\"", 1); - PathExitOnFailure(hr, "Failed to allocate a closing quote."); - - // Add a char for the closing quote. - ++cchPath; - } - - if (fDirectory) - { - if (L'\\' != (*ppszPath)[cchPath - 2]) - { - // Change the last char to a backslash and re-append the closing quote. - (*ppszPath)[cchPath - 1] = L'\\'; - - hr = StrAllocConcat(ppszPath, L"\"", 1); - PathExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); - } - } - -LExit: - - return hr; -} - - -DAPI_(HRESULT) PathCompare( - __in_z LPCWSTR wzPath1, - __in_z LPCWSTR wzPath2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath1 = NULL; - LPWSTR sczPath2 = NULL; - - hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - PathExitOnFailure(hr, "Failed to expand path1."); - - hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - PathExitOnFailure(hr, "Failed to expand path2."); - - *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); - -LExit: - ReleaseStr(sczPath2); - ReleaseStr(sczPath1); - - return hr; -} - - -DAPI_(HRESULT) PathCompress( - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - HANDLE hPath = INVALID_HANDLE_VALUE; - - hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (INVALID_HANDLE_VALUE == hPath) - { - PathExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); - } - - DWORD dwBytesReturned = 0; - USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT; - if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL)) - { - // ignore compression attempts on file systems that don't support it - DWORD er = ::GetLastError(); - if (ERROR_INVALID_FUNCTION != er) - { - PathExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); - } - } - -LExit: - ReleaseFile(hPath); - - return hr; -} - -DAPI_(HRESULT) PathGetHierarchyArray( - __in_z LPCWSTR wzPath, - __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, - __inout LPUINT pcPathArray - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPathCopy = NULL; - LPWSTR sczNewPathCopy = NULL; - DWORD cArraySpacesNeeded = 0; - size_t cchPath = 0; - - hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); - PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath); - - if (!cchPath) - { - ExitFunction1(hr = E_INVALIDARG); - } - - for (size_t i = 0; i < cchPath; ++i) - { - if (wzPath[i] == L'\\') - { - ++cArraySpacesNeeded; - } - } - - if (wzPath[cchPath - 1] != L'\\') - { - ++cArraySpacesNeeded; - } - - // 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. - if (wzPath[0] == L'\\' && wzPath[1] == L'\\') - { - cArraySpacesNeeded -= 3; - } - - Assert(cArraySpacesNeeded >= 1); - - hr = MemEnsureArraySize(reinterpret_cast(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); - PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); - *pcPathArray = cArraySpacesNeeded; - - hr = StrAllocString(&sczPathCopy, wzPath, 0); - PathExitOnFailure(hr, "Failed to allocate copy of original path"); - - for (DWORD i = 0; i < cArraySpacesNeeded; ++i) - { - hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); - PathExitOnFailure(hr, "Failed to copy path"); - - DWORD cchPathCopy = lstrlenW(sczPathCopy); - - // 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 - if (wzPath[cchPathCopy - 1] == L'\\') - { - sczPathCopy[cchPathCopy - 1] = L'\0'; - } - - hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); - PathExitOnFailure(hr, "Failed to get directory portion of path"); - - ReleaseStr(sczPathCopy); - sczPathCopy = sczNewPathCopy; - sczNewPathCopy = NULL; - } - - hr = S_OK; - -LExit: - ReleaseStr(sczPathCopy); - - return hr; -} diff --git a/src/dutil/perfutil.cpp b/src/dutil/perfutil.cpp deleted file mode 100644 index bc138d34..00000000 --- a/src/dutil/perfutil.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PerfExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) -#define PerfExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) -#define PerfExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) -#define PerfExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) -#define PerfExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PERFUTIL, e, x, s, __VA_ARGS__) -#define PerfExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PERFUTIL, g, x, s, __VA_ARGS__) - -static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter -static double vdFrequency = 1; - - -/******************************************************************** - PerfInitialize - initializes internal static variables - -********************************************************************/ -extern "C" void DAPI PerfInitialize( - ) -{ - LARGE_INTEGER liFrequency = { }; - - // - // check for high perf counter - // - if (!::QueryPerformanceFrequency(&liFrequency)) - { - vfHighPerformanceCounter = FALSE; - vdFrequency = 1000; // ticks are measured in milliseconds - } - else - vdFrequency = static_cast(liFrequency.QuadPart); -} - - -/******************************************************************** - PerfClickTime - resets the clicker, or returns elapsed time since last call - - NOTE: if pliElapsed is NULL, resets the elapsed time - if pliElapsed is not NULL, returns perf number since last call to PerfClickTime() -********************************************************************/ -extern "C" void DAPI PerfClickTime( - __out_opt LARGE_INTEGER* pliElapsed - ) -{ - static LARGE_INTEGER liStart = { }; - LARGE_INTEGER* pli = pliElapsed; - - if (!pli) // if elapsed time time was not requested, reset the start time - pli = &liStart; - - if (vfHighPerformanceCounter) - ::QueryPerformanceCounter(pli); - else - pli->QuadPart = ::GetTickCount(); - - if (pliElapsed) - pliElapsed->QuadPart -= liStart.QuadPart; -} - - -/******************************************************************** - PerfConvertToSeconds - converts perf number to seconds - -********************************************************************/ -extern "C" double DAPI PerfConvertToSeconds( - __in const LARGE_INTEGER* pli - ) -{ - Assert(0 < vdFrequency); - return pli->QuadPart / vdFrequency; -} diff --git a/src/dutil/polcutil.cpp b/src/dutil/polcutil.cpp deleted file mode 100644 index 1fdfa18c..00000000 --- a/src/dutil/polcutil.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PolcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) -#define PolcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) -#define PolcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) -#define PolcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) -#define PolcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_POLCUTIL, e, x, s, __VA_ARGS__) -#define PolcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_POLCUTIL, g, x, s, __VA_ARGS__) - -const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\"; - -static HRESULT OpenPolicyKey( - __in_z LPCWSTR wzPolicyPath, - __out HKEY* phk - ); - - -extern "C" HRESULT DAPI PolcReadNumber( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in DWORD dwDefault, - __out DWORD* pdw - ) -{ - HRESULT hr = S_OK; - HKEY hk = NULL; - - hr = OpenPolicyKey(wzPolicyPath, &hk); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); - - hr = RegReadNumber(hk, wzPolicyName, pdw); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); - -LExit: - ReleaseRegKey(hk); - - if (S_FALSE == hr || FAILED(hr)) - { - *pdw = dwDefault; - } - - return hr; -} - -extern "C" HRESULT DAPI PolcReadString( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in_z_opt LPCWSTR wzDefault, - __deref_out_z LPWSTR* pscz - ) -{ - HRESULT hr = S_OK; - HKEY hk = NULL; - - hr = OpenPolicyKey(wzPolicyPath, &hk); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); - - hr = RegReadString(hk, wzPolicyName, pscz); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); - -LExit: - ReleaseRegKey(hk); - - if (S_FALSE == hr || FAILED(hr)) - { - if (NULL == wzDefault) - { - ReleaseNullStr(*pscz); - } - else - { - hr = StrAllocString(pscz, wzDefault, 0); - } - } - - return hr; -} - - -// internal functions - -static HRESULT OpenPolicyKey( - __in_z LPCWSTR wzPolicyPath, - __out HKEY* phk - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - - hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath); - PolcExitOnFailure(hr, "Failed to combine logging path with root path."); - - hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk); - PolcExitOnFailure(hr, "Failed to open policy registry key."); - -LExit: - ReleaseStr(sczPath); - - return hr; -} diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h deleted file mode 100644 index f8f3b944..00000000 --- a/src/dutil/precomp.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0500 -#endif - -#ifndef _WIN32_MSI -#define _WIN32_MSI 200 -#endif - -#define JET_VERSION 0x0501 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dutilsources.h" -#include "dutil.h" -#include "verutil.h" -#include "aclutil.h" -#include "atomutil.h" -#include "buffutil.h" -#include "butil.h" -#include "cabcutil.h" -#include "cabutil.h" -#include "conutil.h" -#include "cryputil.h" -#include "eseutil.h" -#include "dirutil.h" -#include "dlutil.h" -#include "dpiutil.h" -#include "fileutil.h" -#include "guidutil.h" -#include "gdiputil.h" -#include "dictutil.h" -#include "deputil.h" // NOTE: This must come after dictutil.h since it uses it. -#include "inetutil.h" -#include "iniutil.h" -#include "jsonutil.h" -#include "locutil.h" -#include "logutil.h" -#include "memutil.h" // NOTE: almost everying is inlined so there is a small .cpp file -//#include "metautil.h" - see metautil.cpp why this *must* be commented out -#include "monutil.h" -#include "osutil.h" -#include "pathutil.h" -#include "perfutil.h" -#include "polcutil.h" -#include "procutil.h" -#include "regutil.h" -#include "resrutil.h" -#include "reswutil.h" -#include "rmutil.h" -#include "rssutil.h" -#include "apuputil.h" // NOTE: this must come after atomutil.h and rssutil.h since it uses them. -#include "shelutil.h" -//#include "sqlutil.h" - see sqlutil.cpp why this *must* be commented out -#include "srputil.h" -#include "strutil.h" -#include "timeutil.h" -#include "timeutil.h" -#include "thmutil.h" -#include "uncutil.h" -#include "uriutil.h" -#include "userutil.h" -#include "wiutil.h" -#include "wuautil.h" -#include // This header is needed for msxml2.h to compile correctly -#include // This file is needed to include xmlutil.h -#include "xmlutil.h" - diff --git a/src/dutil/proc2utl.cpp b/src/dutil/proc2utl.cpp deleted file mode 100644 index a59d2ffc..00000000 --- a/src/dutil/proc2utl.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) -#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** - ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcFindAllIdsFromExeName( - __in_z LPCWSTR wzExeName, - __out DWORD** ppdwProcessIds, - __out DWORD* pcProcessIds - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - HANDLE hSnap = INVALID_HANDLE_VALUE; - BOOL fContinue = FALSE; - PROCESSENTRY32W peData = { sizeof(peData) }; - - hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (INVALID_HANDLE_VALUE == hSnap) - { - ProcExitWithLastError(hr, "Failed to create snapshot of processes on system"); - } - - fContinue = ::Process32FirstW(hSnap, &peData); - - while (fContinue) - { - if (0 == lstrcmpiW((LPCWSTR)&(peData.szExeFile), wzExeName)) - { - if (!*ppdwProcessIds) - { - *ppdwProcessIds = static_cast(MemAlloc(sizeof(DWORD), TRUE)); - ProcExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs."); - } - else - { - DWORD* pdwReAllocReturnedPids = NULL; - pdwReAllocReturnedPids = static_cast(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE)); - ProcExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs."); - - *ppdwProcessIds = pdwReAllocReturnedPids; - } - - (*ppdwProcessIds)[*pcProcessIds] = peData.th32ProcessID; - ++(*pcProcessIds); - } - - fContinue = ::Process32NextW(hSnap, &peData); - } - - er = ::GetLastError(); - if (ERROR_NO_MORE_FILES == er) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - -LExit: - ReleaseFile(hSnap); - - return hr; -} diff --git a/src/dutil/proc3utl.cpp b/src/dutil/proc3utl.cpp deleted file mode 100644 index 6d3cbc67..00000000 --- a/src/dutil/proc3utl.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) -#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) - -static HRESULT GetActiveSessionUserToken( - __out HANDLE *phToken - ); - - -/******************************************************************** - ProcExecuteAsInteractiveUser() - runs process as currently logged in - user. -*******************************************************************/ -extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser( - __in_z LPCWSTR wzExecutablePath, - __in_z LPCWSTR wzCommandLine, - __out HANDLE *phProcess - ) -{ - HRESULT hr = S_OK; - HANDLE hToken = NULL; - LPVOID pEnvironment = NULL; - LPWSTR sczFullCommandLine = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - - hr = GetActiveSessionUserToken(&hToken); - ProcExitOnFailure(hr, "Failed to get active session user token."); - - if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE)) - { - ProcExitWithLastError(hr, "Failed to create environment block for UI process."); - } - - hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine); - ProcExitOnFailure(hr, "Failed to allocate full command-line."); - - si.cb = sizeof(si); - if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi)) - { - ProcExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine); - } - - *phProcess = pi.hProcess; - pi.hProcess = NULL; - -LExit: - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - ReleaseStr(sczFullCommandLine); - - if (pEnvironment) - { - ::DestroyEnvironmentBlock(pEnvironment); - } - - ReleaseHandle(hToken); - - return hr; -} - - -static HRESULT GetActiveSessionUserToken( - __out HANDLE *phToken - ) -{ - HRESULT hr = S_OK; - PWTS_SESSION_INFO pSessionInfo = NULL; - DWORD cSessions = 0; - DWORD dwSessionId = 0; - BOOL fSessionFound = FALSE; - HANDLE hToken = NULL; - - // Loop through the sessions looking for the active one. - if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions)) - { - ProcExitWithLastError(hr, "Failed to enumerate sessions."); - } - - for (DWORD i = 0; i < cSessions; ++i) - { - if (WTSActive == pSessionInfo[i].State) - { - dwSessionId = pSessionInfo[i].SessionId; - fSessionFound = TRUE; - - break; - } - } - - if (!fSessionFound) - { - ExitFunction1(hr = E_NOTFOUND); - } - - // Get the user token from the active session. - if (!::WTSQueryUserToken(dwSessionId, &hToken)) - { - ProcExitWithLastError(hr, "Failed to get active session user token."); - } - - *phToken = hToken; - hToken = NULL; - -LExit: - ReleaseHandle(hToken); - - if (pSessionInfo) - { - ::WTSFreeMemory(pSessionInfo); - } - - return hr; -} diff --git a/src/dutil/procutil.cpp b/src/dutil/procutil.cpp deleted file mode 100644 index 6bfe5017..00000000 --- a/src/dutil/procutil.cpp +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) -#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) - - -// private functions -static HRESULT CreatePipes( - __out HANDLE *phOutRead, - __out HANDLE *phOutWrite, - __out HANDLE *phErrWrite, - __out HANDLE *phInRead, - __out HANDLE *phInWrite - ); - -static BOOL CALLBACK CloseWindowEnumCallback( - __in HWND hWnd, - __in LPARAM lParam - ); - - -extern "C" HRESULT DAPI ProcElevated( - __in HANDLE hProcess, - __out BOOL* pfElevated - ) -{ - HRESULT hr = S_OK; - HANDLE hToken = NULL; - TOKEN_ELEVATION tokenElevated = { }; - DWORD cbToken = 0; - - if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) - { - ProcExitWithLastError(hr, "Failed to open process token."); - } - - if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) - { - *pfElevated = (0 != tokenElevated.TokenIsElevated); - } - else - { - DWORD er = ::GetLastError(); - hr = HRESULT_FROM_WIN32(er); - - // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated. - if (E_INVALIDARG == hr) - { - *pfElevated = FALSE; - hr = S_OK; - } - else - { - ProcExitOnRootFailure(hr, "Failed to get elevation token from process."); - } - } - -LExit: - ReleaseHandle(hToken); - - return hr; -} - -extern "C" HRESULT DAPI ProcWow64( - __in HANDLE hProcess, - __out BOOL* pfWow64 - ) -{ - HRESULT hr = S_OK; - BOOL fIsWow64 = FALSE; - - typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); - LPFN_ISWOW64PROCESS2 pfnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process2"); - - if (pfnIsWow64Process2) - { - USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN; - if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr)) - { - ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2."); - } - - if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN) - { - fIsWow64 = TRUE; - } - } - else - { - typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); - LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process"); - - if (pfnIsWow64Process) - { - if (!pfnIsWow64Process(hProcess, &fIsWow64)) - { - ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process."); - } - } - } - - *pfWow64 = fIsWow64; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ) -{ - AssertSz(!pfsr->fDisabled, "File system redirection was already disabled."); - HRESULT hr = S_OK; - - typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *); - LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); - - if (!pfnWow64DisableWow64FsRedirection) - { - ExitFunction1(hr = E_NOTIMPL); - } - - if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState)) - { - ProcExitWithLastError(hr, "Failed to disable file system redirection."); - } - - pfsr->fDisabled = TRUE; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ) -{ - HRESULT hr = S_OK; - - if (pfsr->fDisabled) - { - typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID); - LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); - - if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState)) - { - ProcExitWithLastError(hr, "Failed to revert file system redirection."); - } - - pfsr->fDisabled = FALSE; - pfsr->pvRevertState = NULL; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI ProcExec( - __in_z LPCWSTR wzExecutablePath, - __in_z_opt LPCWSTR wzCommandLine, - __in int nCmdShow, - __out HANDLE *phProcess - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFullCommandLine = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - - hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L""); - ProcExitOnFailure(hr, "Failed to allocate full command-line."); - - si.cb = sizeof(si); - si.wShowWindow = static_cast(nCmdShow); - if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) - { - ProcExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine); - } - - *phProcess = pi.hProcess; - pi.hProcess = NULL; - -LExit: - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - ReleaseStr(sczFullCommandLine); - - return hr; -} - - -/******************************************************************** - ProcExecute() - executes a command-line. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcExecute( - __in_z LPWSTR wzCommand, - __out HANDLE *phProcess, - __out_opt HANDLE *phChildStdIn, - __out_opt HANDLE *phChildStdOutErr - ) -{ - HRESULT hr = S_OK; - - PROCESS_INFORMATION pi = { }; - STARTUPINFOW si = { }; - - HANDLE hOutRead = INVALID_HANDLE_VALUE; - HANDLE hOutWrite = INVALID_HANDLE_VALUE; - HANDLE hErrWrite = INVALID_HANDLE_VALUE; - HANDLE hInRead = INVALID_HANDLE_VALUE; - HANDLE hInWrite = INVALID_HANDLE_VALUE; - - // Create redirect pipes. - hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); - ProcExitOnFailure(hr, "failed to create output pipes"); - - // Set up startup structure. - si.cb = sizeof(STARTUPINFOW); - si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = hInRead; - si.hStdOutput = hOutWrite; - si.hStdError = hErrWrite; - -#pragma prefast(push) -#pragma prefast(disable:25028) - if (::CreateProcessW(NULL, - wzCommand, // command line - NULL, // security info - NULL, // thread info - TRUE, // inherit handles - ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags - NULL, // environment - NULL, // cur dir - &si, - &pi)) -#pragma prefast(pop) - { - // Close child process output/input handles so child doesn't hang - // while waiting for input from parent process. - ::CloseHandle(hOutWrite); - hOutWrite = INVALID_HANDLE_VALUE; - - ::CloseHandle(hErrWrite); - hErrWrite = INVALID_HANDLE_VALUE; - - ::CloseHandle(hInRead); - hInRead = INVALID_HANDLE_VALUE; - } - else - { - ProcExitWithLastError(hr, "Process failed to execute."); - } - - *phProcess = pi.hProcess; - pi.hProcess = 0; - - if (phChildStdIn) - { - *phChildStdIn = hInWrite; - hInWrite = INVALID_HANDLE_VALUE; - } - - if (phChildStdOutErr) - { - *phChildStdOutErr = hOutRead; - hOutRead = INVALID_HANDLE_VALUE; - } - -LExit: - if (pi.hThread) - { - ::CloseHandle(pi.hThread); - } - - if (pi.hProcess) - { - ::CloseHandle(pi.hProcess); - } - - ReleaseFileHandle(hOutRead); - ReleaseFileHandle(hOutWrite); - ReleaseFileHandle(hErrWrite); - ReleaseFileHandle(hInRead); - ReleaseFileHandle(hInWrite); - - return hr; -} - - -/******************************************************************** - ProcWaitForCompletion() - waits for process to complete and gets return code. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcWaitForCompletion( - __in HANDLE hProcess, - __in DWORD dwTimeout, - __out DWORD *pReturnCode - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - // Wait for everything to finish - er = ::WaitForSingleObject(hProcess, dwTimeout); - if (WAIT_FAILED == er) - { - ProcExitWithLastError(hr, "Failed to wait for process to complete."); - } - else if (WAIT_TIMEOUT == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - - if (!::GetExitCodeProcess(hProcess, &er)) - { - ProcExitWithLastError(hr, "Failed to get process return code."); - } - - *pReturnCode = er; - -LExit: - return hr; -} - -/******************************************************************** - ProcWaitForIds() - waits for multiple processes to complete. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcWaitForIds( - __in_ecount(cProcessIds) const DWORD rgdwProcessIds[], - __in DWORD cProcessIds, - __in DWORD dwMilliseconds - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - HANDLE hProcess = NULL; - HANDLE * rghProcesses = NULL; - DWORD cProcesses = 0; - - rghProcesses = static_cast(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); - ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); - - for (DWORD i = 0; i < cProcessIds; ++i) - { - hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]); - if (hProcess != NULL) - { - rghProcesses[cProcesses++] = hProcess; - } - } - - er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); - if (WAIT_FAILED == er) - { - ProcExitWithLastError(hr, "Failed to wait for process to complete."); - } - else if (WAIT_TIMEOUT == er) - { - ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); - } - -LExit: - if (rghProcesses) - { - for (DWORD i = 0; i < cProcesses; ++i) - { - if (NULL != rghProcesses[i]) - { - ::CloseHandle(rghProcesses[i]); - } - } - - MemFree(rghProcesses); - } - - return hr; -} - -/******************************************************************** - ProcCloseIds() - sends WM_CLOSE messages to all process ids. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcCloseIds( - __in_ecount(cProcessIds) const DWORD* pdwProcessIds, - __in DWORD cProcessIds - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cProcessIds; ++i) - { - if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i])) - { - ProcExitWithLastError(hr, "Failed to enumerate windows."); - } - } - -LExit: - return hr; -} - - -static HRESULT CreatePipes( - __out HANDLE *phOutRead, - __out HANDLE *phOutWrite, - __out HANDLE *phErrWrite, - __out HANDLE *phInRead, - __out HANDLE *phInWrite - ) -{ - HRESULT hr = S_OK; - SECURITY_ATTRIBUTES sa; - HANDLE hOutTemp = INVALID_HANDLE_VALUE; - HANDLE hInTemp = INVALID_HANDLE_VALUE; - - HANDLE hOutRead = INVALID_HANDLE_VALUE; - HANDLE hOutWrite = INVALID_HANDLE_VALUE; - HANDLE hErrWrite = INVALID_HANDLE_VALUE; - HANDLE hInRead = INVALID_HANDLE_VALUE; - HANDLE hInWrite = INVALID_HANDLE_VALUE; - - // Fill out security structure so we can inherit handles - ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - // Create pipes - if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) - { - ProcExitWithLastError(hr, "failed to create output pipe"); - } - - if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) - { - ProcExitWithLastError(hr, "failed to create input pipe"); - } - - // Duplicate output pipe so standard error and standard output write to the same pipe. - if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - ProcExitWithLastError(hr, "failed to duplicate write handle"); - } - - // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in - // the child process that can't be closed. - if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - ProcExitWithLastError(hr, "failed to duplicate output pipe"); - } - - if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - ProcExitWithLastError(hr, "failed to duplicate input pipe"); - } - - // now that everything has succeeded, assign to the outputs - *phOutRead = hOutRead; - hOutRead = INVALID_HANDLE_VALUE; - - *phOutWrite = hOutWrite; - hOutWrite = INVALID_HANDLE_VALUE; - - *phErrWrite = hErrWrite; - hErrWrite = INVALID_HANDLE_VALUE; - - *phInRead = hInRead; - hInRead = INVALID_HANDLE_VALUE; - - *phInWrite = hInWrite; - hInWrite = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hOutRead); - ReleaseFileHandle(hOutWrite); - ReleaseFileHandle(hErrWrite); - ReleaseFileHandle(hInRead); - ReleaseFileHandle(hInWrite); - ReleaseFileHandle(hOutTemp); - ReleaseFileHandle(hInTemp); - - return hr; -} - - -/******************************************************************** - CloseWindowEnumCallback() - outputs trace and log info - -*******************************************************************/ -static BOOL CALLBACK CloseWindowEnumCallback( - __in HWND hWnd, - __in LPARAM lParam - ) -{ - DWORD dwPid = static_cast(lParam); - DWORD dwProcessId = 0; - - ::GetWindowThreadProcessId(hWnd, &dwProcessId); - if (dwPid == dwProcessId) - { - ::SendMessageW(hWnd, WM_CLOSE, 0, 0); - } - - return TRUE; -} diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp deleted file mode 100644 index cb617932..00000000 --- a/src/dutil/regutil.cpp +++ /dev/null @@ -1,1035 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define RegExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) -#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) -#define RegExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) -#define RegExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) -#define RegExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REGUTIL, e, x, s, __VA_ARGS__) -#define RegExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REGUTIL, g, x, s, __VA_ARGS__) - -static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW; -static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW; -static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL; -static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL; -static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW; -static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW; -static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW; -static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW; -static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; -static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; -static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; - -static HMODULE vhAdvApi32Dll = NULL; -static BOOL vfRegInitialized = FALSE; - -static HRESULT WriteStringToRegistry( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue, - __in DWORD dwType -); - -/******************************************************************** - RegInitialize - initializes regutil - -*********************************************************************/ -extern "C" HRESULT DAPI RegInitialize( - ) -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); - RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); - - // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW - vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); - - if (NULL == vpfnRegDeleteKeyExW) - { - vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; - } - - vfRegInitialized = TRUE; - -LExit: - return hr; -} - - -/******************************************************************** - RegUninitialize - uninitializes regutil - -*********************************************************************/ -extern "C" void DAPI RegUninitialize( - ) -{ - if (vhAdvApi32Dll) - { - ::FreeLibrary(vhAdvApi32Dll); - vhAdvApi32Dll = NULL; - vpfnRegDeleteKeyExWFromLibrary = NULL; - vpfnRegDeleteKeyExW = NULL; - } - - vfRegInitialized = FALSE; -} - - -/******************************************************************** - RegFunctionOverride - overrides the registry functions. Typically used - for unit testing. - -*********************************************************************/ -extern "C" void DAPI RegFunctionOverride( - __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, - __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, - __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, - __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, - __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, - __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, - __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, - __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, - __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW - ) -{ - vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; - vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW; - vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary; - vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW; - vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW; - vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW; - vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; - vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; - vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; -} - - -/******************************************************************** - RegCreate - creates a registry key. - -*********************************************************************/ -extern "C" HRESULT DAPI RegCreate( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); - RegExitOnWin32Error(er, hr, "Failed to create registry key."); - -LExit: - return hr; -} - - -/******************************************************************** - RegCreate - creates a registry key with extra options. - -*********************************************************************/ -HRESULT DAPI RegCreateEx( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __in BOOL fVolatile, - __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, - __out HKEY* phk, - __out_opt BOOL* pfCreated - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwDisposition; - - er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition); - RegExitOnWin32Error(er, hr, "Failed to create registry key."); - - if (pfCreated) - { - *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition); - } - -LExit: - return hr; -} - - -/******************************************************************** - RegOpen - opens a registry key. - -*********************************************************************/ -extern "C" HRESULT DAPI RegOpen( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to open registry key."); - -LExit: - return hr; -} - - -/******************************************************************** - RegDelete - deletes a registry key (and optionally it's whole tree). - -*********************************************************************/ -extern "C" HRESULT DAPI RegDelete( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fDeleteTree - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR pszEnumeratedSubKey = NULL; - LPWSTR pszRecursiveSubKey = NULL; - HKEY hkKey = NULL; - REGSAM samDesired = 0; - - if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) - { - hr = E_INVALIDARG; - RegExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); - } - - switch (kbKeyBitness) - { - case REG_KEY_32BIT: - samDesired = KEY_WOW64_32KEY; - break; - case REG_KEY_64BIT: - samDesired = KEY_WOW64_64KEY; - break; - case REG_KEY_DEFAULT: - // Nothing to do - break; - } - - if (fDeleteTree) - { - hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - RegExitOnFailure(hr, "Failed to open this key for enumerating subkeys: %ls", wzSubKey); - - // Yes, keep enumerating the 0th item, because we're deleting it every time - while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) - { - RegExitOnFailure(hr, "Failed to enumerate key 0"); - - hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); - RegExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); - - hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); - RegExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); - } - - hr = S_OK; - } - - if (NULL != vpfnRegDeleteKeyExW) - { - er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); - } - else - { - er = vpfnRegDeleteKeyW(hkRoot, wzSubKey); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to delete registry key."); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(pszEnumeratedSubKey); - ReleaseStr(pszRecursiveSubKey); - - return hr; -} - - -/******************************************************************** - RegKeyEnum - enumerates child registry keys. - -*********************************************************************/ -extern "C" HRESULT DAPI RegKeyEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczKey - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - SIZE_T cb = 0; - DWORD cch = 0; - - if (psczKey && *psczKey) - { - hr = StrMaxLength(*psczKey, &cb); - RegExitOnFailure(hr, "Failed to determine length of string."); - - cch = (DWORD)min(DWORD_MAX, cb); - } - - if (2 > cch) - { - cch = 2; - - hr = StrAlloc(psczKey, cch); - RegExitOnFailure(hr, "Failed to allocate string to minimum size."); - } - - er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); - if (ERROR_MORE_DATA == er) - { - er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL); - RegExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); - - ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator. - hr = StrAlloc(psczKey, cch); - RegExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); - - er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); - } - else if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = E_NOMOREITEMS); - } - RegExitOnWin32Error(er, hr, "Failed to enum registry key."); - - // Always ensure the registry key name is null terminated. -#pragma prefast(push) -#pragma prefast(disable:26018) - (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works. -#pragma prefast(pop) - -LExit: - return hr; -} - - -/******************************************************************** - RegValueEnum - enumerates registry values. - -*********************************************************************/ -HRESULT DAPI RegValueEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczName, - __out_opt DWORD *pdwType - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD cbValueName = 0; - - er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL); - RegExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); - - // Add one for null terminator - ++cbValueName; - - hr = StrAlloc(psczName, cbValueName); - RegExitOnFailure(hr, "Failed to allocate array for registry value name"); - - er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = E_NOMOREITEMS); - } - RegExitOnWin32Error(er, hr, "Failed to enumerate registry value"); - -LExit: - return hr; -} - -/******************************************************************** - RegGetType - reads a registry key value type. - *********************************************************************/ -HRESULT DAPI RegGetType( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD *pdwType - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry value."); -LExit: - - return hr; -} - -/******************************************************************** - RegReadBinary - reads a registry key binary value. - NOTE: caller is responsible for freeing *ppbBuffer -*********************************************************************/ -HRESULT DAPI RegReadBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, - __out SIZE_T *pcbBuffer - ) -{ - HRESULT hr = S_OK; - LPBYTE pbBuffer = NULL; - DWORD er = ERROR_SUCCESS; - DWORD cb = 0; - DWORD dwType = 0; - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); - RegExitOnWin32Error(er, hr, "Failed to get size of registry value."); - - // Zero-length binary values can exist - if (0 < cb) - { - pbBuffer = static_cast(MemAlloc(cb, FALSE)); - RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry value."); - } - - if (REG_BINARY == dwType) - { - *ppbBuffer = pbBuffer; - pbBuffer = NULL; - *pcbBuffer = cb; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); - } - -LExit: - ReleaseMem(pbBuffer); - - return hr; -} - - -/******************************************************************** - RegReadString - reads a registry key value as a string. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - SIZE_T cbValue = 0; - DWORD cch = 0; - DWORD cb = 0; - DWORD dwType = 0; - LPWSTR sczExpand = NULL; - - if (psczValue && *psczValue) - { - hr = StrMaxLength(*psczValue, &cbValue); - RegExitOnFailure(hr, "Failed to determine length of string."); - - cch = (DWORD)min(DWORD_MAX, cbValue); - } - - if (2 > cch) - { - cch = 2; - - hr = StrAlloc(psczValue, cch); - RegExitOnFailure(hr, "Failed to allocate string to minimum size."); - } - - cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); - if (ERROR_MORE_DATA == er) - { - cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator - hr = StrAlloc(psczValue, cch); - RegExitOnFailure(hr, "Failed to allocate string bigger for registry value."); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); - } - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry key."); - - if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) - { - // Always ensure the registry value is null terminated. - (*psczValue)[cch - 1] = L'\0'; - - if (REG_EXPAND_SZ == dwType) - { - hr = StrAllocString(&sczExpand, *psczValue, 0); - RegExitOnFailure(hr, "Failed to copy registry value to expand."); - - hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); - RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); - } - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); - } - -LExit: - ReleaseStr(sczExpand); - - return hr; -} - - -/******************************************************************** - RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. - -*********************************************************************/ -HRESULT DAPI RegReadStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, - __out DWORD *pcStrings - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwNullCharacters = 0; - DWORD dwType = 0; - DWORD cb = 0; - DWORD cch = 0; - LPCWSTR wzSource = NULL; - LPWSTR sczValue = NULL; - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); - if (0 < cb) - { - cch = cb / sizeof(WCHAR); - hr = StrAlloc(&sczValue, cch); - RegExitOnFailure(hr, "Failed to allocate string for registry value."); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); - } - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry key."); - - if (cb / sizeof(WCHAR) != cch) - { - hr = E_UNEXPECTED; - RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); - } - - if (REG_MULTI_SZ != dwType) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); - } - - // Value exists, but is empty, so no strings to return. - if (2 > cch) - { - *prgsczStrings = NULL; - *pcStrings = 0; - ExitFunction1(hr = S_OK); - } - - // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. - if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) - { - hr = E_INVALIDARG; - RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); - } - - cch = cb / sizeof(WCHAR); - for (DWORD i = 0; i < cch; ++i) - { - if (L'\0' == sczValue[i]) - { - ++dwNullCharacters; - } - } - - // There's one string for every null character encountered (except the extra 1 at the end of the string) - *pcStrings = dwNullCharacters - 1; - hr = MemEnsureArraySize(reinterpret_cast(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); - RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); - -#pragma prefast(push) -#pragma prefast(disable:26010) - wzSource = sczValue; - for (DWORD i = 0; i < *pcStrings; ++i) - { - hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); - RegExitOnFailure(hr, "Failed to allocate copy of string"); - - // Skip past this string - wzSource += lstrlenW(wzSource) + 1; - } -#pragma prefast(pop) - -LExit: - ReleaseStr(sczValue); - - return hr; -} - - -/******************************************************************** - RegReadVersion - reads a registry key value as a version. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadVersion( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pdw64Version - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwType = 0; - DWORD cb = 0; - LPWSTR sczVersion = NULL; - - cb = sizeof(DWORD64); - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*pdw64Version), &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) - { - hr = RegReadString(hk, wzName, &sczVersion); - RegExitOnFailure(hr, "Failed to read registry version as string."); - - hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); - RegExitOnFailure(hr, "Failed to convert registry string to version."); - } - else if (REG_QWORD == dwType) - { - RegExitOnWin32Error(er, hr, "Failed to read registry key."); - } - else // unexpected data type - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); - } - -LExit: - ReleaseStr(sczVersion); - - return hr; -} - - -/******************************************************************** - RegReadNumber - reads a DWORD registry key value as a number. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD* pdwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwType = 0; - DWORD cb = sizeof(DWORD); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pdwValue), &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to query registry key value."); - - if (REG_DWORD != dwType) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); - } - -LExit: - return hr; -} - - -/******************************************************************** - RegReadQword - reads a QWORD registry key value as a number. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pqwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwType = 0; - DWORD cb = sizeof(DWORD64); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pqwValue), &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to query registry key value."); - - if (REG_QWORD != dwType) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); - } - -LExit: - return hr; -} - - -/******************************************************************** - RegWriteBinary - writes a registry key value as a binary. - -*********************************************************************/ -HRESULT DAPI RegWriteBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_bcount(cbBuffer) const BYTE *pbBuffer, - __in DWORD cbBuffer - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer); - RegExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); - -LExit: - return hr; -} - - -/******************************************************************** -RegWriteExpandString - writes a registry key value as an expand string. - -Note: if wzValue is NULL the value will be removed. -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteExpandString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue -) -{ - return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ); -} - - -/******************************************************************** - RegWriteString - writes a registry key value as a string. - - Note: if wzValue is NULL the value will be removed. -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue - ) -{ - return WriteStringToRegistry(hk, wzName, wzValue, REG_SZ); -} - - -/******************************************************************** - RegWriteStringFormatted - writes a registry key value as a formatted string. - -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteStringFormatted( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in __format_string LPCWSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - va_list args; - - va_start(args, szFormat); - hr = StrAllocFormattedArgs(&sczValue, szFormat, args); - va_end(args); - RegExitOnFailure(hr, "Failed to allocate %ls value.", wzName); - - hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ); - -LExit: - ReleaseStr(sczValue); - - return hr; -} - - -/******************************************************************** - RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value - -*********************************************************************/ -HRESULT DAPI RegWriteStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_ecount(cValues) LPWSTR *rgwzValues, - __in DWORD cValues - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR wzCopyDestination = NULL; - LPCWSTR wzWriteValue = NULL; - LPWSTR sczWriteValue = NULL; - DWORD dwTotalStringSize = 0; - DWORD cbTotalStringSize = 0; - DWORD dwTemp = 0; - - if (0 == cValues) - { - wzWriteValue = L"\0"; - } - else - { - // Add space for the null terminator - dwTotalStringSize = 1; - - for (DWORD i = 0; i < cValues; ++i) - { - dwTemp = dwTotalStringSize; - hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize); - RegExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); - } - - hr = StrAlloc(&sczWriteValue, dwTotalStringSize); - RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); - - wzCopyDestination = sczWriteValue; - dwTemp = dwTotalStringSize; - for (DWORD i = 0; i < cValues; ++i) - { - hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); - RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); - - dwTemp -= lstrlenW(rgwzValues[i]) + 1; - wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; - } - - wzWriteValue = sczWriteValue; - } - - hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); - RegExitOnFailure(hr, "Failed to get total string size in bytes"); - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast(wzWriteValue), cbTotalStringSize); - RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); - -LExit: - ReleaseStr(sczWriteValue); - - return hr; -} - -/******************************************************************** - RegWriteNumber - writes a registry key value as a number. - -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof(dwValue)); - RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); - -LExit: - return hr; -} - -/******************************************************************** - RegWriteQword - writes a registry key value as a Qword. - -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD64 qwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast(&qwValue), sizeof(qwValue)); - RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); - -LExit: - return hr; -} - -/******************************************************************** - RegQueryKey - queries the key for the number of subkeys and values. - -*********************************************************************/ -extern "C" HRESULT DAPI RegQueryKey( - __in HKEY hk, - __out_opt DWORD* pcSubKeys, - __out_opt DWORD* pcValues - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL); - RegExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); - -LExit: - return hr; -} - -/******************************************************************** -RegKeyReadNumber - reads a DWORD registry key value as a number from -a specified subkey. - -*********************************************************************/ -extern "C" HRESULT DAPI RegKeyReadNumber( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit, - __out DWORD* pdwValue - ) -{ - HRESULT hr = S_OK; - HKEY hkKey = NULL; - - hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); - RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); - - hr = RegReadNumber(hkKey, wzName, pdwValue); - RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName); - -LExit: - ReleaseRegKey(hkKey); - - return hr; -} - -/******************************************************************** -RegValueExists - determines whether a named value exists in a -specified subkey. - -*********************************************************************/ -extern "C" BOOL DAPI RegValueExists( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit - ) -{ - HRESULT hr = S_OK; - HKEY hkKey = NULL; - DWORD dwType = 0; - - hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); - RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); - - hr = RegGetType(hkKey, wzName, &dwType); - RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName); - -LExit: - ReleaseRegKey(hkKey); - - return SUCCEEDED(hr); -} - -static HRESULT WriteStringToRegistry( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue, - __in DWORD dwType - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - size_t cbValue = 0; - - if (wzValue) - { - hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue); - RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); - - er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), static_cast(cbValue)); - RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); - } - else - { - er = vpfnRegDeleteValueW(hk, wzName); - if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) - { - er = ERROR_SUCCESS; - } - RegExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); - } - -LExit: - return hr; -} diff --git a/src/dutil/resrutil.cpp b/src/dutil/resrutil.cpp deleted file mode 100644 index a6a7ee23..00000000 --- a/src/dutil/resrutil.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ResrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) -#define ResrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) -#define ResrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) -#define ResrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) -#define ResrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESRUTIL, e, x, s, __VA_ARGS__) -#define ResrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESRUTIL, g, x, s, __VA_ARGS__) - -#define RES_STRINGS_PER_BLOCK 16 - - -BOOL CALLBACK EnumLangIdProc( - __in_opt HMODULE hModule, - __in_z LPCSTR lpType, - __in_z LPCSTR lpName, - __in WORD wLanguage, - __in LONG_PTR lParam - ); - -/******************************************************************** -ResGetStringLangId - get the language id for a string in the string table. - -********************************************************************/ -extern "C" HRESULT DAPI ResGetStringLangId( - __in_opt LPCWSTR wzPath, - __in UINT uID, - __out WORD *pwLangId - ) -{ - Assert(pwLangId); - - HRESULT hr = S_OK; - HINSTANCE hModule = NULL; - DWORD dwBlockId = (uID / RES_STRINGS_PER_BLOCK) + 1; - WORD wFoundLangId = 0; - - if (wzPath && *wzPath) - { - hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); - ResrExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath); - } - -#pragma prefast(push) -#pragma prefast(disable:25068) - if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast(EnumLangIdProc), reinterpret_cast(&wFoundLangId))) -#pragma prefast(pop) - { - ResrExitWithLastError(hr, "Failed to find string language identifier."); - } - - *pwLangId = wFoundLangId; - -LExit: - if (hModule) - { - ::FreeLibrary(hModule); - } - - return hr; -} - - -/******************************************************************** -ResReadString - -NOTE: ppwzString should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI ResReadString( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPWSTR* ppwzString - ) -{ - Assert(hinst && ppwzString); - - HRESULT hr = S_OK; - DWORD cch = 64; // first guess - DWORD cchReturned = 0; - - do - { - hr = StrAlloc(ppwzString, cch); - ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); - - cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch); - if (0 == cchReturned) - { - ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); - } - - // if the returned string count is one character too small, it's likely we have - // more data to read - if (cchReturned + 1 == cch) - { - cch *= 2; - hr = S_FALSE; - } - } while (S_FALSE == hr); - ResrExitOnFailure(hr, "Failed to load string resource id: %d", uID); - -LExit: - return hr; -} - - -/******************************************************************** - ResReadStringAnsi - - NOTE: ppszString should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI ResReadStringAnsi( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPSTR* ppszString - ) -{ - Assert(hinst && ppszString); - - HRESULT hr = S_OK; - DWORD cch = 64; // first guess - DWORD cchReturned = 0; - - do - { - hr = StrAnsiAlloc(ppszString, cch); - ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); - -#pragma prefast(push) -#pragma prefast(disable:25068) - cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch); -#pragma prefast(pop) - if (0 == cchReturned) - { - ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); - } - - // if the returned string count is one character too small, it's likely we have - // more data to read - if (cchReturned + 1 == cch) - { - cch *= 2; - hr = S_FALSE; - } - } while (S_FALSE == hr); - ResrExitOnFailure(hr, "failed to load string resource id: %d", uID); - -LExit: - return hr; -} - - -/******************************************************************** -ResReadData - returns a pointer to the specified resource data - -NOTE: there is no "free" function for this call -********************************************************************/ -extern "C" HRESULT DAPI ResReadData( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szDataName, - __deref_out_bcount(*pcb) PVOID *ppv, - __out DWORD *pcb - ) -{ - Assert(szDataName); - Assert(ppv); - - HRESULT hr = S_OK; - HRSRC hRsrc = NULL; - HGLOBAL hData = NULL; - DWORD cbData = 0; - -#pragma prefast(push) -#pragma prefast(disable:25068) - hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); -#pragma prefast(pop) - ResrExitOnNullWithLastError(hRsrc, hr, "Failed to find resource."); - - hData = ::LoadResource(hinst, hRsrc); - ResrExitOnNullWithLastError(hData, hr, "Failed to load resource."); - - cbData = ::SizeofResource(hinst, hRsrc); - if (!cbData) - { - ResrExitWithLastError(hr, "Failed to get size of resource."); - } - - *ppv = ::LockResource(hData); - ResrExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource."); - *pcb = cbData; - -LExit: - return hr; -} - - -/******************************************************************** -ResExportDataToFile - extracts the resource data to the specified target file - -********************************************************************/ -extern "C" HRESULT DAPI ResExportDataToFile( - __in_z LPCSTR szDataName, - __in_z LPCWSTR wzTargetFile, - __in DWORD dwCreationDisposition - ) -{ - HRESULT hr = S_OK; - PVOID pData = NULL; - DWORD cbData = 0; - DWORD cbWritten = 0; - HANDLE hFile = INVALID_HANDLE_VALUE; - BOOL bCreatedFile = FALSE; - - hr = ResReadData(NULL, szDataName, &pData, &cbData); - ResrExitOnFailure(hr, "Failed to GetData from %s.", szDataName); - - hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ResrExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile); - } - bCreatedFile = TRUE; - - if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL)) - { - ResrExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile); - } - -LExit: - ReleaseFile(hFile); - - if (FAILED(hr)) - { - if (bCreatedFile) - { - ::DeleteFileW(wzTargetFile); - } - } - - return hr; -} - - -BOOL CALLBACK EnumLangIdProc( - __in_opt HMODULE /* hModule */, - __in_z LPCSTR /* lpType */, - __in_z LPCSTR /* lpName */, - __in WORD wLanguage, - __in LONG_PTR lParam - ) -{ - WORD *pwLangId = reinterpret_cast(lParam); - - *pwLangId = wLanguage; - return TRUE; -} diff --git a/src/dutil/reswutil.cpp b/src/dutil/reswutil.cpp deleted file mode 100644 index e78de84a..00000000 --- a/src/dutil/reswutil.cpp +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ReswExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) -#define ReswExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) -#define ReswExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) -#define ReswExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) -#define ReswExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESWUTIL, e, x, s, __VA_ARGS__) -#define ReswExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESWUTIL, g, x, s, __VA_ARGS__) - -#define RES_STRINGS_PER_BLOCK 16 - -// Internal data structure format for a string block in a resource table. -// Note: Strings are always stored as UNICODED. -typedef struct _RES_STRING_BLOCK -{ - DWORD dwBlockId; - WORD wLangId; - LPWSTR rgwz[RES_STRINGS_PER_BLOCK]; -} RES_STRING_BLOCK; - - -// private functions -static HRESULT StringBlockInitialize( - __in_opt HINSTANCE hModule, - __in DWORD dwBlockId, - __in WORD wLangId, - __in RES_STRING_BLOCK* pStrBlock - ); -static void StringBlockUnitialize( - __in RES_STRING_BLOCK* pStrBlock - ); -static HRESULT StringBlockChangeString( - __in RES_STRING_BLOCK* pStrBlock, - __in DWORD dwStringId, - __in_z LPCWSTR szData - ); -static HRESULT StringBlockConvertToResourceData( - __in const RES_STRING_BLOCK* pStrBlock, - __deref_out_bcount(*pcbData) LPVOID* ppvData, - __out DWORD* pcbData - ); -static HRESULT StringBlockConvertFromResourceData( - __in RES_STRING_BLOCK* pStrBlock, - __in_bcount(cbData) LPCVOID pvData, - __in SIZE_T cbData - ); - - -/******************************************************************** -ResWriteString - sets the string into to the specified file's resource name - -********************************************************************/ -extern "C" HRESULT DAPI ResWriteString( - __in_z LPCWSTR wzResourceFile, - __in DWORD dwDataId, - __in_z LPCWSTR wzData, - __in WORD wLangId - ) -{ - Assert(wzResourceFile); - Assert(wzData); - - HRESULT hr = S_OK; - HINSTANCE hModule = NULL; - HANDLE hUpdate = NULL; - RES_STRING_BLOCK StrBlock = { }; - LPVOID pvData = NULL; - DWORD cbData = 0; - - DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1; - DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK); - - hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); - ReswExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); - - hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock); - ReswExitOnFailure(hr, "Failed to get string block to update."); - - hr = StringBlockChangeString(&StrBlock, dwStringId, wzData); - ReswExitOnFailure(hr, "Failed to update string block string."); - - hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData); - ReswExitOnFailure(hr, "Failed to convert string block to resource data."); - - ::FreeLibrary(hModule); - hModule = NULL; - - hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); - ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); - - if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData)) - { - ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); - } - - if (!::EndUpdateResource(hUpdate, FALSE)) - { - ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); - } - - hUpdate = NULL; - -LExit: - ReleaseMem(pvData); - - StringBlockUnitialize(&StrBlock); - - if (hUpdate) - { - ::EndUpdateResource(hUpdate, TRUE); - } - - if (hModule) - { - ::FreeLibrary(hModule); - } - - return hr; -} - - -/******************************************************************** -ResWriteData - sets the data into to the specified file's resource name - -********************************************************************/ -extern "C" HRESULT DAPI ResWriteData( - __in_z LPCWSTR wzResourceFile, - __in_z LPCSTR szDataName, - __in PVOID pData, - __in DWORD cbData - ) -{ - Assert(wzResourceFile); - Assert(szDataName); - Assert(pData); - Assert(cbData); - - HRESULT hr = S_OK; - HANDLE hUpdate = NULL; - - hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); - ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); - - if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData)) - { - ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); - } - - if (!::EndUpdateResource(hUpdate, FALSE)) - { - ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); - } - - hUpdate = NULL; - -LExit: - if (hUpdate) - { - ::EndUpdateResource(hUpdate, TRUE); - } - - return hr; -} - - -/******************************************************************** -ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name - -********************************************************************/ -extern "C" HRESULT DAPI ResImportDataFromFile( - __in_z LPCWSTR wzTargetFile, - __in_z LPCWSTR wzSourceFile, - __in_z LPCSTR szDataName - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - DWORD cbFile = 0; - HANDLE hMap = NULL; - PVOID pv = NULL; - - hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ReswExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); - } - - cbFile = ::GetFileSize(hFile, NULL); - if (!cbFile) - { - ReswExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); - } - - hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - ReswExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); - - pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile); - ReswExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); - - hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile); - ReswExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); - -LExit: - if (pv) - { - ::UnmapViewOfFile(pv); - } - - if (hMap) - { - ::CloseHandle(hMap); - } - - ReleaseFile(hFile); - - return hr; -} - - -static HRESULT StringBlockInitialize( - __in_opt HINSTANCE hModule, - __in DWORD dwBlockId, - __in WORD wLangId, - __in RES_STRING_BLOCK* pStrBlock - ) -{ - HRESULT hr = S_OK; - HRSRC hRsrc = NULL; - HGLOBAL hData = NULL; - LPCVOID pvData = NULL; // does not need to be freed - DWORD cbData = 0; - - hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId); - ReswExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); - - hData = ::LoadResource(hModule, hRsrc); - ReswExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); - - cbData = ::SizeofResource(hModule, hRsrc); - if (!cbData) - { - ReswExitWithLastError(hr, "Failed to ::SizeofResource."); - } - - pvData = ::LockResource(hData); - ReswExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); - - pStrBlock->dwBlockId = dwBlockId; - pStrBlock->wLangId = wLangId; - - hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData); - ReswExitOnFailure(hr, "Failed to convert string block from resource data."); - -LExit: - return hr; -} - - -static void StringBlockUnitialize( - __in RES_STRING_BLOCK* pStrBlock - ) -{ - if (pStrBlock) - { - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - ReleaseNullMem(pStrBlock->rgwz[i]); - } - } -} - - -static HRESULT StringBlockChangeString( - __in RES_STRING_BLOCK* pStrBlock, - __in DWORD dwStringId, - __in_z LPCWSTR szData - ) -{ - HRESULT hr = S_OK; - LPWSTR pwzData = NULL; - size_t cchData = 0; - - hr = ::StringCchLengthW(szData, STRSAFE_MAX_LENGTH, &cchData); - ReswExitOnRootFailure(hr, "Failed to get block string length."); - - pwzData = static_cast(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); - ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); - - hr = ::StringCchCopyW(pwzData, cchData + 1, szData); - ReswExitOnRootFailure(hr, "Failed to copy new block string."); - - ReleaseNullMem(pStrBlock->rgwz[dwStringId]); - - pStrBlock->rgwz[dwStringId] = pwzData; - pwzData = NULL; - -LExit: - ReleaseMem(pwzData); - - return hr; -} - - -static HRESULT StringBlockConvertToResourceData( - __in const RES_STRING_BLOCK* pStrBlock, - __deref_out_bcount(*pcbData) LPVOID* ppvData, - __out DWORD* pcbData - ) -{ - HRESULT hr = S_OK; - DWORD cbData = 0; - LPVOID pvData = NULL; - WCHAR* pwz = NULL; - - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1); - } - cbData *= sizeof(WCHAR); - - pvData = MemAlloc(cbData, TRUE); - ReswExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); - - pwz = static_cast(pvData); - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - DWORD cch = lstrlenW(pStrBlock->rgwz[i]); - - *pwz = static_cast(cch); - ++pwz; - - for (DWORD j = 0; j < cch; ++j) - { - *pwz = pStrBlock->rgwz[i][j]; - ++pwz; - } - } - - *pcbData = cbData; - *ppvData = pvData; - pvData = NULL; - -LExit: - ReleaseMem(pvData); - - return hr; -} - - -static HRESULT StringBlockConvertFromResourceData( - __in RES_STRING_BLOCK* pStrBlock, - __in_bcount(cbData) LPCVOID pvData, - __in SIZE_T cbData - ) -{ - UNREFERENCED_PARAMETER(cbData); - HRESULT hr = S_OK; - LPCWSTR pwzParse = static_cast(pvData); - - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - DWORD cchParse = static_cast(*pwzParse); - ++pwzParse; - - pStrBlock->rgwz[i] = static_cast(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE)); - ReswExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); - - hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ReswExitOnFailure(hr, "Failed to copy parsed resource data into string block."); - - pwzParse += cchParse; - } - -LExit: - return hr; -} diff --git a/src/dutil/rexutil.cpp b/src/dutil/rexutil.cpp deleted file mode 100644 index 155ca714..00000000 --- a/src/dutil/rexutil.cpp +++ /dev/null @@ -1,601 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#include "rexutil.h" - - -// Exit macros -#define RexExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) -#define RexExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) -#define RexExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) -#define RexExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) -#define RexExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REXUTIL, e, x, s, __VA_ARGS__) -#define RexExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REXUTIL, g, x, s, __VA_ARGS__) - -// -// static globals -// -static HMODULE vhCabinetDll = NULL; -static HFDI vhfdi = NULL; -static ERF verf; - -static FAKE_FILE vrgffFileTable[FILETABLESIZE]; -static DWORD vcbRes; -static LPCBYTE vpbRes; -static CHAR vszResource[MAX_PATH]; -static REX_CALLBACK_WRITE vpfnWrite = NULL; - -static HRESULT vhrLastError = S_OK; - -// -// structs -// -struct REX_CALLBACK_STRUCT -{ - BOOL fStopExtracting; // flag set when no more files are needed - LPCWSTR pwzExtract; // file to extract ("*" means extract all) - LPCWSTR pwzExtractDir; // directory to extract files to - LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*") - - // possible user data - REX_CALLBACK_PROGRESS pfnProgress; - LPVOID pvContext; -}; - -// -// prototypes -// -static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize); -static __callback void DIAMONDAPI RexFree(LPVOID pvData); -static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode); -static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb); -static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb); -static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf); -static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype); -static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify); - - -/******************************************************************** - RexInitialize - initializes internal static variables - -*******************************************************************/ -extern "C" HRESULT RexInitialize() -{ - Assert(!vhfdi); - - HRESULT hr = S_OK; - - vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf); - if (!vhfdi) - { - hr = E_FAIL; - RexExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here - } - - ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable)); - -LExit: - if (FAILED(hr)) - { - ::FDIDestroy(vhfdi); - vhfdi = NULL; - } - - return hr; -} - - -/******************************************************************** - RexUninitialize - initializes internal static variables - -*******************************************************************/ -extern "C" void RexUninitialize() -{ - if (vhfdi) - { - ::FDIDestroy(vhfdi); - vhfdi = NULL; - } -} - - -/******************************************************************** - RexExtract - extracts one or all files from a resource cabinet - - NOTE: wzExtractId can be a single file id or "*" to extract all files - wzExttractDir must be normalized (end in a "\") - wzExtractName is ignored if wzExtractId is "*" -*******************************************************************/ -extern "C" HRESULT RexExtract( - __in_z LPCSTR szResource, - __in_z LPCWSTR wzExtractId, - __in_z LPCWSTR wzExtractDir, - __in_z LPCWSTR wzExtractName, - __in REX_CALLBACK_PROGRESS pfnProgress, - __in REX_CALLBACK_WRITE pfnWrite, - __in LPVOID pvContext - ) -{ - Assert(vhfdi); - HRESULT hr = S_OK; - BOOL fResult; - - HRSRC hResInfo = NULL; - HANDLE hRes = NULL; - - REX_CALLBACK_STRUCT rcs; - - // remember the write callback - vpfnWrite = pfnWrite; - - // - // load the cabinet resource - // - hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); - RexExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); - //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10)); - //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info"); - - hRes = ::LoadResource(NULL, hResInfo); - RexExitOnNullWithLastError(hRes, hr, "failed to load resource"); - - vcbRes = ::SizeofResource(NULL, hResInfo); - vpbRes = (const BYTE*)::LockResource(hRes); - - // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it - - // - // convert the resource name to multi-byte - // - //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL)) - //{ - // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); - //} - - hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); - RexExitOnFailure(hr, "Failed to copy resource name to global."); - - // - // iterate through files in cabinet extracting them to the callback function - // - rcs.fStopExtracting = FALSE; - rcs.pwzExtract = wzExtractId; - rcs.pwzExtractDir = wzExtractDir; - rcs.pwzExtractName = wzExtractName; - rcs.pfnProgress = pfnProgress; - rcs.pvContext = pvContext; - - fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast(&rcs)); - if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure - { - hr = vhrLastError; // TODO: put verf info in trace message here - } - -LExit: - return hr; -} - - -/**************************************************************************** - default extract routines - -****************************************************************************/ -static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize) -{ - return MemAlloc(dwSize, FALSE); -} - - -static __callback void DIAMONDAPI RexFree(LPVOID pvData) -{ - MemFree(pvData); -} - - -static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - int i = 0; - - // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail - if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) - { - hr = E_OUTOFMEMORY; - RexExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); - } - - // find an empty spot in the fake file table - for (i = 0; i < FILETABLESIZE; ++i) - { - if (!vrgffFileTable[i].fUsed) - { - break; - } - } - - // we should never run out of space in the fake file table - if (FILETABLESIZE <= i) - { - hr = E_OUTOFMEMORY; - RexExitOnFailure(hr, "File table exceeded"); - } - - if (0 == lstrcmpA(vszResource, pszFile)) - { - vrgffFileTable[i].fUsed = TRUE; - vrgffFileTable[i].fftType = MEMORY_FILE; - vrgffFileTable[i].mfFile.vpStart = static_cast(vpbRes); - vrgffFileTable[i].mfFile.uiCurrent = 0; - vrgffFileTable[i].mfFile.uiLength = vcbRes; - } - else // it's a real file - { - hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - RexExitWithLastError(hr, "failed to open file: %s", pszFile); - } - - vrgffFileTable[i].fUsed = TRUE; - vrgffFileTable[i].fftType = NORMAL_FILE; - vrgffFileTable[i].hFile = hFile; - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : i; -} - - -static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb) -{ - Assert(vrgffFileTable[hf].fUsed); - - HRESULT hr = S_OK; - DWORD cbRead = 0; - DWORD cbAvailable = 0; - - if (MEMORY_FILE == vrgffFileTable[hf].fftType) - { - // ensure that we don't read past the length of the resource - cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent; - cbRead = cb < cbAvailable? cb : cbAvailable; - - memcpy(pv, static_cast(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead); - - vrgffFileTable[hf].mfFile.uiCurrent += cbRead; - } - else // NORMAL_FILE - { - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - - if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL)) - { - RexExitWithLastError(hr, "failed to read during cabinet extraction"); - } - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : cbRead; -} - - -static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb) -{ - Assert(vrgffFileTable[hf].fUsed); - Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file - - HRESULT hr = S_OK; - DWORD cbWrite = 0; - - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - if (!::WriteFile(reinterpret_cast(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL)) - { - RexExitWithLastError(hr, "failed to write during cabinet extraction"); - } - - // call the writer callback if defined - if (vpfnWrite) - { - vpfnWrite(cb); - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : cbWrite; -} - - -static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype) -{ - Assert(vrgffFileTable[hf].fUsed); - - HRESULT hr = S_OK; - DWORD dwMoveMethod; - LONG lMove = 0; - - switch (seektype) - { - case 0: // SEEK_SET - dwMoveMethod = FILE_BEGIN; - break; - case 1: /// SEEK_CUR - dwMoveMethod = FILE_CURRENT; - break; - case 2: // SEEK_END - dwMoveMethod = FILE_END; - break; - default : - dwMoveMethod = 0; - hr = E_UNEXPECTED; - RexExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); - } - - if (MEMORY_FILE == vrgffFileTable[hf].fftType) - { - if (FILE_BEGIN == dwMoveMethod) - { - vrgffFileTable[hf].mfFile.uiCurrent = dist; - } - else if (FILE_CURRENT == dwMoveMethod) - { - vrgffFileTable[hf].mfFile.uiCurrent += dist; - } - else // FILE_END - { - vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist; - } - - lMove = vrgffFileTable[hf].mfFile.uiCurrent; - } - else // NORMAL_FILE - { - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - - // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. - // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) - lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod); - if (0xFFFFFFFF == lMove) - { - RexExitWithLastError(hr, "failed to move file pointer %d bytes", dist); - } - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : lMove; -} - - -__callback int FAR DIAMONDAPI RexClose(INT_PTR hf) -{ - Assert(vrgffFileTable[hf].fUsed); - - HRESULT hr = S_OK; - - if (MEMORY_FILE == vrgffFileTable[hf].fftType) - { - vrgffFileTable[hf].mfFile.vpStart = NULL; - vrgffFileTable[hf].mfFile.uiCurrent = 0; - vrgffFileTable[hf].mfFile.uiLength = 0; - } - else - { - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - - if (!::CloseHandle(vrgffFileTable[hf].hFile)) - { - RexExitWithLastError(hr, "failed to close file during cabinet extraction"); - } - - vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE; - } - - vrgffFileTable[hf].fUsed = FALSE; - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : 0; -} - - -static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify) -{ - Assert(pFDINotify->pv); - - HRESULT hr = S_OK; - int ipResult = 0; // result to return on success - HANDLE hFile = INVALID_HANDLE_VALUE; - - REX_CALLBACK_STRUCT* prcs = static_cast(pFDINotify->pv); - LPCSTR sz; - WCHAR wz[MAX_PATH]; - FILETIME ft; - int i = 0; - - switch (iNotification) - { - case fdintCOPY_FILE: // beGIN extracting a resource from cabinet - Assert(pFDINotify->psz1); - - if (prcs->fStopExtracting) - { - ExitFunction1(hr = S_FALSE); // no more extracting - } - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - if (prcs->pfnProgress) - { - hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); - if (S_OK != hr) - { - ExitFunction(); - } - } - - if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) - { - // get the created date for the resource in the cabinet - if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) - { - RexExitWithLastError(hr, "failed to get time for resource: %ls", wz); - } - - WCHAR wzPath[MAX_PATH]; - - hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); - RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); - - if (L'*' == *prcs->pwzExtract) - { - hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); - } - else - { - Assert(*prcs->pwzExtractName); - - hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); - } - - // Quickly chop off the file name part of the path to ensure the path exists - // then put the file name back on the path (by putting the first character - // back over the null terminator). - LPWSTR wzFile = PathFile(wzPath); - WCHAR wzFileFirstChar = *wzFile; - *wzFile = L'\0'; - - hr = DirEnsureExists(wzPath, NULL); - RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); - - hr = S_OK; - - *wzFile = wzFileFirstChar; - - // find an empty spot in the fake file table - for (i = 0; i < FILETABLESIZE; ++i) - { - if (!vrgffFileTable[i].fUsed) - { - break; - } - } - - // we should never run out of space in the fake file table - if (FILETABLESIZE <= i) - { - hr = E_OUTOFMEMORY; - RexExitOnFailure(hr, "File table exceeded"); - } - - // open the file - hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - RexExitWithLastError(hr, "failed to open file: %ls", wzPath); - } - - vrgffFileTable[i].fUsed = TRUE; - vrgffFileTable[i].fftType = NORMAL_FILE; - vrgffFileTable[i].hFile = hFile; - - ipResult = i; - - ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) - - if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) - { - if (::SetEndOfFile(vrgffFileTable[i].hFile)) - { - ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer - } - } - } - else // resource wasn't requested, skip it - { - hr = S_OK; - ipResult = 0; - } - - break; - case fdintCLOSE_FILE_INFO: // resource extraction complete - Assert(pFDINotify->hf && pFDINotify->psz1); - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - RexClose(pFDINotify->hf); - - if (prcs->pfnProgress) - { - hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); - } - - if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going - { - ipResult = TRUE; - } - else // something went wrong or we only needed to extract one file - { - hr = S_OK; - ipResult = FALSE; - prcs->fStopExtracting = TRUE; - } - - break; - case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through - case fdintNEXT_CABINET: __fallthrough; - case fdintENUMERATE: __fallthrough; - case fdintCABINET_INFO: - break; - default: - AssertSz(FALSE, "RexCallback() - unknown FDI notification command"); - }; - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return (S_OK == hr) ? ipResult : -1; -} diff --git a/src/dutil/rmutil.cpp b/src/dutil/rmutil.cpp deleted file mode 100644 index 95c8c8a4..00000000 --- a/src/dutil/rmutil.cpp +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#include - - -// Exit macros -#define RmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) -#define RmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) -#define RmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) -#define RmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) -#define RmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RMUTIL, e, x, s, __VA_ARGS__) -#define RmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RMUTIL, g, x, s, __VA_ARGS__) - -#define ARRAY_GROWTH_SIZE 5 - -typedef DWORD (WINAPI *PFNRMJOINSESSION)( - __out DWORD *pSessionHandle, - __in_z const WCHAR strSessionKey[] - ); - -typedef DWORD (WINAPI *PFNRMENDSESSION)( - __in DWORD dwSessionHandle - ); - -typedef DWORD (WINAPI *PFNRMREGISTERRESOURCES)( - __in DWORD dwSessionHandle, - __in UINT nFiles, - __in_z_opt LPWSTR *rgsFilenames, - __in UINT nApplications, - __in_opt RM_UNIQUE_PROCESS *rgApplications, - __in UINT nServices, - __in_z_opt LPWSTR *rgsServiceNames - ); - -typedef struct _RMU_SESSION -{ - CRITICAL_SECTION cs; - DWORD dwSessionHandle; - BOOL fStartedSessionHandle; - BOOL fInitialized; - - UINT cFilenames; - LPWSTR *rgsczFilenames; - - UINT cApplications; - RM_UNIQUE_PROCESS *rgApplications; - - UINT cServiceNames; - LPWSTR *rgsczServiceNames; - -} RMU_SESSION; - -static volatile LONG vcRmuInitialized = 0; -static HMODULE vhModule = NULL; -static PFNRMJOINSESSION vpfnRmJoinSession = NULL; -static PFNRMENDSESSION vpfnRmEndSession = NULL; -static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL; - -static HRESULT RmuInitialize(); -static void RmuUninitialize(); - -static HRESULT RmuApplicationArrayAlloc( - __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, - __inout LPUINT pcApplications, - __in DWORD dwProcessId, - __in FILETIME ProcessStartTime - ); - -static HRESULT RmuApplicationArrayFree( - __in RM_UNIQUE_PROCESS *rgApplications - ); - -#define ReleaseNullApplicationArray(rg, c) { if (rg) { RmuApplicationArrayFree(rg); c = 0; rg = NULL; } } - -/******************************************************************** -RmuJoinSession - Joins an existing Restart Manager session. - -********************************************************************/ -extern "C" HRESULT DAPI RmuJoinSession( - __out PRMU_SESSION *ppSession, - __in_z LPCWSTR wzSessionKey - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - PRMU_SESSION pSession = NULL; - - *ppSession = NULL; - - pSession = static_cast(MemAlloc(sizeof(RMU_SESSION), TRUE)); - RmExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); - - hr = RmuInitialize(); - RmExitOnFailure(hr, "Failed to initialize Restart Manager."); - - er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey); - RmExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); - - ::InitializeCriticalSection(&pSession->cs); - pSession->fInitialized = TRUE; - - *ppSession = pSession; - -LExit: - if (FAILED(hr)) - { - ReleaseNullMem(pSession); - } - - return hr; -} - -/******************************************************************** -RmuAddFile - Adds the file path to the Restart Manager session. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddFile( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - - ::EnterCriticalSection(&pSession->cs); - - // Create or grow the jagged array. - hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0); - RmExitOnFailure(hr, "Failed to add the filename to the array."); - -LExit: - ::LeaveCriticalSection(&pSession->cs); - return hr; -} - -/******************************************************************** -RmuAddProcessById - Adds the process ID to the Restart Manager sesion. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddProcessById( - __in PRMU_SESSION pSession, - __in DWORD dwProcessId - ) -{ - HRESULT hr = S_OK; - HANDLE hProcess = NULL; - FILETIME CreationTime = {}; - FILETIME ExitTime = {}; - FILETIME KernelTime = {}; - FILETIME UserTime = {}; - BOOL fLocked = FALSE; - - HANDLE hToken = NULL; - TOKEN_PRIVILEGES priv = { 0 }; - TOKEN_PRIVILEGES* pPrevPriv = NULL; - DWORD cbPrevPriv = 0; - DWORD er = ERROR_SUCCESS; - BOOL fAdjustedPrivileges = FALSE; - BOOL fElevated = FALSE; - ProcElevated(::GetCurrentProcess(), &fElevated); - - // Must be elevated to adjust process privileges - if (fElevated) { - // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context. - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) - { - RmExitWithLastError(hr, "Failed to get process token."); - } - - priv.PrivilegeCount = 1; - priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) - { - RmExitWithLastError(hr, "Failed to get debug privilege LUID."); - } - - cbPrevPriv = sizeof(TOKEN_PRIVILEGES); - pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); - RmExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); - - if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) - { - LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); - RmExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); - pPrevPriv = static_cast(pv); - - if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) - { - RmExitWithLastError(hr, "Failed to get debug privilege LUID."); - } - } - - fAdjustedPrivileges = TRUE; - } - - hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); - if (hProcess) - { - if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime)) - { - RmExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId); - } - - ::EnterCriticalSection(&pSession->cs); - fLocked = TRUE; - hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime); - RmExitOnFailure(hr, "Failed to add the application to the array."); - } - else - { - er = ::GetLastError(); - if (ERROR_ACCESS_DENIED == er) - { - // OpenProcess will fail when not elevated and the target process is in another user context. Let the caller log and continue. - hr = E_NOTFOUND; - } - else - { - RmExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId); - } - } - -LExit: - if (hProcess) - { - ::CloseHandle(hProcess); - } - - if (fAdjustedPrivileges) - { - ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL); - } - - ReleaseMem(pPrevPriv); - ReleaseHandle(hToken); - - if (fLocked) - { - ::LeaveCriticalSection(&pSession->cs); - } - - return hr; -} - -/******************************************************************** -RmuAddProcessesByName - Adds all processes by the given process name - to the Restart Manager Session. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddProcessesByName( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzProcessName - ) -{ - HRESULT hr = S_OK; - DWORD *pdwProcessIds = NULL; - DWORD cProcessIds = 0; - BOOL fNotFound = FALSE; - - hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds); - RmExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName); - - for (DWORD i = 0; i < cProcessIds; ++i) - { - hr = RmuAddProcessById(pSession, pdwProcessIds[i]); - if (E_NOTFOUND == hr) - { - // RmuAddProcessById returns E_NOTFOUND when this setup is not elevated and OpenProcess returned access denied (target process running under another user account). - fNotFound = TRUE; - } - else - { - RmExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]); - } - } - - // 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. - if (fNotFound) - { - hr = E_NOTFOUND; - } - -LExit: - ReleaseMem(pdwProcessIds); - - return hr; -} - -/******************************************************************** -RmuAddService - Adds the service name to the Restart Manager session. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddService( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzServiceName - ) -{ - HRESULT hr = S_OK; - - ::EnterCriticalSection(&pSession->cs); - - hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0); - RmExitOnFailure(hr, "Failed to add the service name to the array."); - -LExit: - ::LeaveCriticalSection(&pSession->cs); - return hr; -} - -/******************************************************************** -RmuRegisterResources - Registers resources for the Restart Manager. - -This should be called rarely because it is expensive to run. Call -functions like RmuAddFile for multiple resources then commit them -as a batch of updates to RmuRegisterResources. - -Duplicate resources appear to be handled by Restart Manager. -Only one WM_QUERYENDSESSION is being sent for each top-level window. - -********************************************************************/ -extern "C" HRESULT DAPI RmuRegisterResources( - __in PRMU_SESSION pSession - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); - - ::EnterCriticalSection(&pSession->cs); - - er = vpfnRmRegisterResources( - pSession->dwSessionHandle, - pSession->cFilenames, - pSession->rgsczFilenames, - pSession->cApplications, - pSession->rgApplications, - pSession->cServiceNames, - pSession->rgsczServiceNames - ); - RmExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); - - // Empty the arrays if registered in case additional resources are added later. - ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); - ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); - ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); - -LExit: - ::LeaveCriticalSection(&pSession->cs); - return hr; -} - -/******************************************************************** -RmuEndSession - Ends the session. - -If the session was joined by RmuJoinSession, any remaining resources -are registered before the session is ended (left). - -********************************************************************/ -extern "C" HRESULT DAPI RmuEndSession( - __in PRMU_SESSION pSession - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); - - // Make sure all resources are registered if we joined the session. - if (!pSession->fStartedSessionHandle) - { - hr = RmuRegisterResources(pSession); - RmExitOnFailure(hr, "Failed to register remaining resources."); - } - - er = vpfnRmEndSession(pSession->dwSessionHandle); - RmExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); - -LExit: - if (pSession->fInitialized) - { - ::DeleteCriticalSection(&pSession->cs); - } - - ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); - ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); - ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); - ReleaseNullMem(pSession); - - RmuUninitialize(); - - return hr; -} - -static HRESULT RmuInitialize() -{ - HRESULT hr = S_OK; - HMODULE hModule = NULL; - - LONG iRef = ::InterlockedIncrement(&vcRmuInitialized); - if (1 == iRef && !vhModule) - { - hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule); - RmExitOnFailure(hr, "Failed to load the rstrtmgr.dll module."); - - vpfnRmJoinSession = reinterpret_cast(::GetProcAddress(hModule, "RmJoinSession")); - RmExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll."); - - vpfnRmRegisterResources = reinterpret_cast(::GetProcAddress(hModule, "RmRegisterResources")); - RmExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll."); - - vpfnRmEndSession = reinterpret_cast(::GetProcAddress(hModule, "RmEndSession")); - RmExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll."); - - vhModule = hModule; - } - -LExit: - return hr; -} - -static void RmuUninitialize() -{ - LONG iRef = ::InterlockedDecrement(&vcRmuInitialized); - if (0 == iRef && vhModule) - { - vpfnRmJoinSession = NULL; - vpfnRmEndSession = NULL; - vpfnRmRegisterResources = NULL; - - ::FreeLibrary(vhModule); - vhModule = NULL; - } -} - -static HRESULT RmuApplicationArrayAlloc( - __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, - __inout LPUINT pcApplications, - __in DWORD dwProcessId, - __in FILETIME ProcessStartTime - ) -{ - HRESULT hr = S_OK; - RM_UNIQUE_PROCESS *pApplication = NULL; - - hr = MemEnsureArraySize(reinterpret_cast(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE); - RmExitOnFailure(hr, "Failed to allocate memory for the application array."); - - pApplication = static_cast(&(*prgApplications)[*pcApplications]); - pApplication->dwProcessId = dwProcessId; - pApplication->ProcessStartTime = ProcessStartTime; - - ++(*pcApplications); - -LExit: - return hr; -} - -static HRESULT RmuApplicationArrayFree( - __in RM_UNIQUE_PROCESS *rgApplications - ) -{ - HRESULT hr = S_OK; - - hr = MemFree(rgApplications); - RmExitOnFailure(hr, "Failed to free memory for the application array."); - -LExit: - return hr; -} diff --git a/src/dutil/rssutil.cpp b/src/dutil/rssutil.cpp deleted file mode 100644 index 8f994dfc..00000000 --- a/src/dutil/rssutil.cpp +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define RssExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) -#define RssExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) -#define RssExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) -#define RssExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) -#define RssExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RSSUTIL, e, x, s, __VA_ARGS__) -#define RssExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RSSUTIL, g, x, s, __VA_ARGS__) - -static HRESULT ParseRssDocument( - __in IXMLDOMDocument *pixd, - __out RSS_CHANNEL **ppChannel - ); -static HRESULT ParseRssChannel( - __in IXMLDOMNode *pixnChannel, - __out RSS_CHANNEL **ppChannel - ); -static HRESULT ParseRssItem( - __in IXMLDOMNode *pixnItem, - __in DWORD cItem, - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ); -static HRESULT ParseRssUnknownElement( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement - ); -static HRESULT ParseRssUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ); -static void FreeRssUnknownElementList( - __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement - ); -static void FreeRssUnknownAttributeList( - __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ); - - -/******************************************************************** - RssInitialize - Initialize RSS utilities. - -*********************************************************************/ -extern "C" HRESULT DAPI RssInitialize() -{ - return XmlInitialize(); -} - - -/******************************************************************** - RssUninitialize - Uninitialize RSS utilities. - -*********************************************************************/ -extern "C" void DAPI RssUninitialize() -{ - XmlUninitialize(); -} - - -/******************************************************************** - RssParseFromString - parses out an RSS channel from a string. - -*********************************************************************/ -extern "C" HRESULT DAPI RssParseFromString( - __in_z LPCWSTR wzRssString, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(wzRssString); - Assert(ppChannel); - - HRESULT hr = S_OK; - RSS_CHANNEL *pNewChannel = NULL; - IXMLDOMDocument *pixdRss = NULL; - - hr = XmlLoadDocument(wzRssString, &pixdRss); - RssExitOnFailure(hr, "Failed to load RSS string as XML document."); - - hr = ParseRssDocument(pixdRss, &pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS document."); - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseObject(pixdRss); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - RssParseFromFile - parses out an RSS channel from a file path. - -*********************************************************************/ -extern "C" HRESULT DAPI RssParseFromFile( - __in_z LPCWSTR wzRssFile, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(wzRssFile); - Assert(ppChannel); - - HRESULT hr = S_OK; - RSS_CHANNEL *pNewChannel = NULL; - IXMLDOMDocument *pixdRss = NULL; - - hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss); - RssExitOnFailure(hr, "Failed to load RSS string as XML document."); - - hr = ParseRssDocument(pixdRss, &pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS document."); - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseObject(pixdRss); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - RssFreeChannel - parses out an RSS channel from a string. - -*********************************************************************/ -extern "C" void DAPI RssFreeChannel( - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ) -{ - if (pChannel) - { - for (DWORD i = 0; i < pChannel->cItems; ++i) - { - ReleaseStr(pChannel->rgItems[i].wzTitle); - ReleaseStr(pChannel->rgItems[i].wzLink); - ReleaseStr(pChannel->rgItems[i].wzDescription); - ReleaseStr(pChannel->rgItems[i].wzGuid); - ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl); - ReleaseStr(pChannel->rgItems[i].wzEnclosureType); - - FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements); - } - - ReleaseStr(pChannel->wzTitle); - ReleaseStr(pChannel->wzLink); - ReleaseStr(pChannel->wzDescription); - FreeRssUnknownElementList(pChannel->pUnknownElements); - - MemFree(pChannel); - } -} - - -/******************************************************************** - ParseRssDocument - parses out an RSS channel from a loaded XML DOM document. - -*********************************************************************/ -static HRESULT ParseRssDocument( - __in IXMLDOMDocument *pixd, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(pixd); - Assert(ppChannel); - - HRESULT hr = S_OK; - IXMLDOMElement *pRssElement = NULL; - IXMLDOMNodeList *pChannelNodes = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - RSS_CHANNEL *pNewChannel = NULL; - - // - // Get the document element and start processing channels. - // - hr = pixd ->get_documentElement(&pRssElement); - RssExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); - - hr = pRssElement->get_childNodes(&pChannelNodes); - RssExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); - - while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName))) - { - if (0 == lstrcmpW(bstrNodeName, L"channel")) - { - hr = ParseRssChannel(pNode, &pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS channel."); - } - else if (0 == lstrcmpW(bstrNodeName, L"link")) - { - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - - if (S_FALSE == hr) - { - hr = S_OK; - } - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pChannelNodes); - ReleaseObject(pRssElement); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - ParseRssChannel - parses out an RSS channel from a loaded XML DOM element. - -*********************************************************************/ -static HRESULT ParseRssChannel( - __in IXMLDOMNode *pixnChannel, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(pixnChannel); - Assert(ppChannel); - - HRESULT hr = S_OK; - IXMLDOMNodeList *pNodeList = NULL; - - RSS_CHANNEL *pNewChannel = NULL; - long cItems = 0; - - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - - // - // First, calculate how many RSS items we're going to have and allocate - // the RSS_CHANNEL structure - // - hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList); - RssExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); - - hr = pNodeList->get_length(&cItems); - RssExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); - - pNewChannel = static_cast(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE)); - RssExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); - - pNewChannel->cItems = cItems; - - // - // Process the elements under a channel now. - // - hr = pixnChannel->get_childNodes(&pNodeList); - RssExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); - - cItems = 0; // reset the counter and use this to walk through the channel items - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (0 == lstrcmpW(bstrNodeName, L"title")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel title."); - - hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS channel title."); - } - else if (0 == lstrcmpW(bstrNodeName, L"link")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel link."); - - hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS channel link."); - } - else if (0 == lstrcmpW(bstrNodeName, L"description")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel description."); - - hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS channel description."); - } - else if (0 == lstrcmpW(bstrNodeName, L"ttl")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel description."); - - pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10); - } - else if (0 == lstrcmpW(bstrNodeName, L"item")) - { - hr = ParseRssItem(pNode, cItems, pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS item."); - - ++cItems; - } - else - { - hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements); - RssExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeValue); - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - ParseRssItem - parses out an RSS item from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseRssItem( - __in IXMLDOMNode *pixnItem, - __in DWORD cItem, - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ) -{ - HRESULT hr = S_OK; - - RSS_ITEM *pItem = NULL; - IXMLDOMNodeList *pNodeList = NULL; - - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - - // - // First make sure we're dealing with a valid item. - // - if (pChannel->cItems <= cItem) - { - hr = E_UNEXPECTED; - RssExitOnFailure(hr, "Unexpected number of items parsed."); - } - - pItem = pChannel->rgItems + cItem; - - // - // Process the elements under an item now. - // - hr = pixnItem->get_childNodes(&pNodeList); - RssExitOnFailure(hr, "Failed to get child nodes of RSS item element."); - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (0 == lstrcmpW(bstrNodeName, L"title")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel title."); - - hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item title."); - } - else if (0 == lstrcmpW(bstrNodeName, L"link")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel link."); - - hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item link."); - } - else if (0 == lstrcmpW(bstrNodeName, L"description")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item description."); - - hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item description."); - } - else if (0 == lstrcmpW(bstrNodeName, L"guid")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item guid."); - - hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item guid."); - } - else if (0 == lstrcmpW(bstrNodeName, L"pubDate")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item guid."); - - hr = TimeFromString(bstrNodeValue, &pItem->ftPublished); - RssExitOnFailure(hr, "Failed to convert RSS item time."); - } - else if (0 == lstrcmpW(bstrNodeName, L"enclosure")) - { - hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item enclosure url."); - - hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); - ReleaseNullBSTR(bstrNodeValue); - - hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize); - RssExitOnFailure(hr, "Failed to get RSS item enclosure length."); - - hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item enclosure type."); - - hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); - } - else - { - hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements); - RssExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeValue); - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - -LExit: - ReleaseBSTR(bstrNodeValue); - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - return hr; -} - - -/******************************************************************** - ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseRssUnknownElement( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement - ) -{ - Assert(ppUnknownElement); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - RSS_UNKNOWN_ELEMENT* pNewUnknownElement; - - pNewUnknownElement = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE)); - RssExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - RssExitOnFailure(hr, "Failed to get unknown element namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - RssExitOnFailure(hr, "Failed to get unknown element name."); - - hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown element name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get unknown element value."); - - hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown element value."); - - hr = pNode->get_attributes(&pixnnmAttributes); - RssExitOnFailure(hr, "Failed get attributes on RSS unknown element."); - - while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) - { - hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); - RssExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); - - ReleaseNullObject(pixnAttribute); - } - - if (S_FALSE == hr) - { - hr = S_OK; - } - RssExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); - - RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownElement; - pNewUnknownElement = NULL; - -LExit: - FreeRssUnknownElementList(pNewUnknownElement); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - - return hr; -} - - -/******************************************************************** - ParseRssUnknownAttribute - parses out attribute from an unknown element - -*********************************************************************/ -static HRESULT ParseRssUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ) -{ - Assert(ppUnknownAttribute); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; - - pNewUnknownAttribute = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE)); - RssExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - RssExitOnFailure(hr, "Failed to get unknown attribute namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - RssExitOnFailure(hr, "Failed to get unknown attribute name."); - - hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get unknown attribute value."); - - hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); - - RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownAttribute; - pNewUnknownAttribute = NULL; - -LExit: - FreeRssUnknownAttributeList(pNewUnknownAttribute); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - - return hr; -} - - -/******************************************************************** - FreeRssUnknownElement - releases all of the memory used by a list of unknown elements - -*********************************************************************/ -static void FreeRssUnknownElementList( - __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement - ) -{ - while (pUnknownElement) - { - RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement; - pUnknownElement = pUnknownElement->pNext; - - FreeRssUnknownAttributeList(pFree->pAttributes); - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzElement); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} - - -/******************************************************************** - FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes - -*********************************************************************/ -static void FreeRssUnknownAttributeList( - __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ) -{ - while (pUnknownAttribute) - { - RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; - pUnknownAttribute = pUnknownAttribute->pNext; - - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzAttribute); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} diff --git a/src/dutil/sceutil.cpp b/src/dutil/sceutil.cpp deleted file mode 100644 index cdb1623b..00000000 --- a/src/dutil/sceutil.cpp +++ /dev/null @@ -1,2489 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#include "sceutil.h" - -// Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB. -#define MAX_SQLCE_DATABASE_SIZE 4091 - -// In case of some older versions of sqlce_oledb.h don't have these definitions, define some types. -#ifndef DBTYPEFOR_DBLENGTH -#ifdef _WIN64 -#define SKIP_SCE_COMPILE -#else -#define SCE_32BIT_ONLY -typedef DWORD DBLENGTH; -typedef LONG DBROWOFFSET; -typedef LONG DBROWCOUNT; -typedef DWORD DBCOUNTITEM; -typedef DWORD DBORDINAL; -typedef LONG DB_LORDINAL; -typedef DWORD DBBKMARK; -typedef DWORD DBBYTEOFFSET; -typedef DWORD DBREFCOUNT; -typedef DWORD DB_UPARAMS; -typedef LONG DB_LPARAMS; -typedef DWORD DBHASHVALUE; -typedef DWORD DB_DWRESERVE; -typedef LONG DB_LRESERVE; -typedef DWORD DB_URESERVE; -#endif - -#endif - -#ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit - -// structs -struct SCE_DATABASE_INTERNAL -{ - // In case we call DllGetClassObject on a specific file - HMODULE hSqlCeDll; - - volatile LONG dwTransactionRefcount; - IDBInitialize *pIDBInitialize; - IDBCreateSession *pIDBCreateSession; - ITransactionLocal *pITransactionLocal; - IDBProperties *pIDBProperties; - IOpenRowset *pIOpenRowset; - ISessionProperties *pISessionProperties; - - BOOL fChanges; // This database has changed - BOOL fPendingChanges; // Some changes are pending, upon transaction commit - BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back - BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail - - // If the database was opened as read-only, we copied it here - so delete it on close - LPWSTR sczTempDbFile; -}; - -struct SCE_ROW -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal; - - SCE_TABLE_SCHEMA *pTableSchema; - IRowset *pIRowset; - HROW hRow; - BOOL fInserting; - - DWORD dwBindingIndex; - DBBINDING *rgBinding; - SIZE_T cbOffset; - BYTE *pbData; -}; - -struct SCE_QUERY -{ - SCE_TABLE_SCHEMA *pTableSchema; - SCE_INDEX_SCHEMA *pIndexSchema; - SCE_DATABASE_INTERNAL *pDatabaseInternal; - - // Accessor build-up members - DWORD dwBindingIndex; - DBBINDING *rgBinding; - SIZE_T cbOffset; - BYTE *pbData; -}; - -struct SCE_QUERY_RESULTS -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal; - IRowset *pIRowset; - SCE_TABLE_SCHEMA *pTableSchema; -}; - -extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW); -extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY); -extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS); - -// The following is the internal Sce-maintained table to tell the identifier and version of the schema -const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] = -{ - { - L"AppIdentifier", - DBTYPE_WSTR, - 0, - FALSE, - TRUE, - FALSE, - NULL, - 0, - 0 - }, - { - L"Version", - DBTYPE_I4, - 0, - FALSE, - FALSE, - FALSE, - NULL, - 0, - 0 - } -}; - -const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] = -{ - L"SceSchemaTablev1", - _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA), - (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA, - 0, - NULL, - NULL, - NULL -}; - -// internal function declarations - -// Creates an instance of SQL Server CE object, returning IDBInitialize object -// If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject -// on that file specifically. Otherwise it calls CoCreateInstance -static HRESULT CreateSqlCe( - __in_z_opt LPCWSTR wzSqlCeDllPath, - __out IDBInitialize **ppIDBInitialize, - __out_opt HMODULE *phSqlCeDll - ); -static HRESULT RunQuery( - __in BOOL fRange, - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, - __out SCE_QUERY_RESULTS **ppsqrhHandle - ); -static HRESULT FillOutColumnDescFromSchema( - __in const SCE_COLUMN_SCHEMA *pSchema, - __out DBCOLUMNDESC pColumnDesc - ); -static HRESULT EnsureSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pDatabaseSchema - ); -static HRESULT OpenSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pdsSchema - ); -static HRESULT SetColumnValue( - __in const SCE_TABLE_SCHEMA *pTableSchema, - __in DWORD dwColumnIndex, - __in_bcount_opt(cbSize) const BYTE *pbData, - __in SIZE_T cbSize, - __inout DBBINDING *pBinding, - __inout SIZE_T *pcbOffset, - __inout BYTE **ppbBuffer - ); -static HRESULT GetColumnValue( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbData, - __out SIZE_T *pcbSize - ); -static HRESULT GetColumnValueFixed( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __in DWORD cbSize, - __out BYTE *pbData - ); -static HRESULT EnsureLocalColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema - ); -static HRESULT EnsureForeignColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema, - __in SCE_DATABASE_SCHEMA *pDatabaseSchema - ); -static HRESULT SetSessionProperties( - __in ISessionProperties *pISessionProperties - ); -static HRESULT GetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __out LPWSTR *psczSchemaType, - __out DWORD *pdwVersion - ); -static HRESULT SetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __in LPCWSTR wzSchemaType, - __in DWORD dwVersion - ); -static void ReleaseDatabase( - SCE_DATABASE *pDatabase - ); -static void ReleaseDatabaseInternal( - SCE_DATABASE_INTERNAL *pDatabaseInternal - ); - -// function definitions -extern "C" HRESULT DAPI SceCreateDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __out SCE_DATABASE **ppDatabase - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDirectory = NULL; - SCE_DATABASE *pNewSceDatabase = NULL; - SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; - IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL; - DBPROPSET rgdbpDataSourcePropSet[2] = { }; - DBPROP rgdbpDataSourceProp[2] = { }; - DBPROP rgdbpDataSourceSsceProp[1] = { }; - - pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); - ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); - - pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); - ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); - - pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); - - hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); - ExitOnFailure(hr, "Failed to get IDBInitialize interface"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast(&pIDBDataSourceAdmin)); - ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface"); - - hr = PathGetDirectory(sczFile, &sczDirectory); - ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile); - - hr = DirEnsureExists(sczDirectory, NULL); - ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory); - - rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; - rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; - rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile); - - rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; - rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[1].vValue.vt = VT_I4; - rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; - - // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT - rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; - rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; - rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); - - rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; - rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; - rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE; - - rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; - rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; - rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); - - hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL); - ExitOnFailure(hr, "Failed to create data source"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); - ExitOnFailure(hr, "Failed to get IDBProperties interface"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); - ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); - - hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); - ExitOnFailure(hr, "Failed to get ISessionProperties interface"); - - hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); - ExitOnFailure(hr, "Failed to set session properties"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); - ExitOnFailure(hr, "Failed to get IOpenRowset interface"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); - ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); - - *ppDatabase = pNewSceDatabase; - pNewSceDatabase = NULL; - -LExit: - ReleaseStr(sczDirectory); - ReleaseObject(pIDBDataSourceAdmin); - ReleaseDatabase(pNewSceDatabase); - ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); - - return hr; -} - -extern "C" HRESULT DAPI SceOpenDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzExpectedSchemaType, - __in DWORD dwExpectedVersion, - __out SCE_DATABASE **ppDatabase, - __in BOOL fReadOnly - ) -{ - HRESULT hr = S_OK; - DWORD dwVersionFound = 0; - WCHAR wzTempDbFile[MAX_PATH]; - LPCWSTR wzPathToOpen = NULL; - LPWSTR sczSchemaType = NULL; - SCE_DATABASE *pNewSceDatabase = NULL; - SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; - DBPROPSET rgdbpDataSourcePropSet[2] = { }; - DBPROP rgdbpDataSourceProp[2] = { }; - DBPROP rgdbpDataSourceSsceProp[1] = { }; - - pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); - ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); - - pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); - ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); - - pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); - - hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); - ExitOnFailure(hr, "Failed to get IDBInitialize interface"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); - ExitOnFailure(hr, "Failed to get IDBProperties interface"); - - // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now. - if (fReadOnly) - { - hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile)); - ExitOnFailure(hr, "Failed to get temp path"); - - hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE); - ExitOnFailure(hr, "Failed to copy file to temp path"); - - hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0); - ExitOnFailure(hr, "Failed to copy temp db file path"); - - wzPathToOpen = (LPCWSTR)wzTempDbFile; - } - else - { - wzPathToOpen = sczFile; - } - - rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; - rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; - rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen); - - rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; - rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[1].vValue.vt = VT_I4; - rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; - - // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT - rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; - rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; - rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); - - rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; - rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; - rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE; - - rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; - rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; - rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); - - hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet); - ExitOnFailure(hr, "Failed to set initial properties to open database"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize(); - ExitOnFailure(hr, "Failed to open database: %ls", sczFile); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); - ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); - - hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); - ExitOnFailure(hr, "Failed to get ISessionProperties interface"); - - hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); - ExitOnFailure(hr, "Failed to set session properties"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); - ExitOnFailure(hr, "Failed to get IOpenRowset interface"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); - ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); - - hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound); - ExitOnFailure(hr, "Failed to find schema version of database"); - - if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1)) - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE); - ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType); - } - else if (dwVersionFound != dwExpectedVersion) - { - hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); - ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound); - } - - *ppDatabase = pNewSceDatabase; - pNewSceDatabase = NULL; - -LExit: - ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); - ReleaseStr(sczSchemaType); - ReleaseDatabase(pNewSceDatabase); - - return hr; -} - -extern "C" HRESULT DAPI SceEnsureDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzSchemaType, - __in DWORD dwExpectedVersion, - __in SCE_DATABASE_SCHEMA *pdsSchema, - __out SCE_DATABASE **ppDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE *pDatabase = NULL; - - if (FileExistsEx(sczFile, NULL)) - { - hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE); - ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile); - } - else - { - hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase); - ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile); - - hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion); - ExitOnFailure(hr, "Failed to set schema version of database"); - } - - hr = EnsureSchema(pDatabase, pdsSchema); - ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile); - - // Keep a pointer to the schema in the SCE_DATABASE object for future reference - pDatabase->pdsSchema = pdsSchema; - - *ppDatabase = pDatabase; - pDatabase = NULL; - -LExit: - ReleaseDatabase(pDatabase); - - return hr; -} - -extern "C" HRESULT DAPI SceIsTableEmpty( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out BOOL *pfEmpty - ) -{ - HRESULT hr = S_OK; - SCE_ROW_HANDLE row = NULL; - - hr = SceGetFirstRow(pDatabase, dwTableIndex, &row); - if (E_NOTFOUND == hr) - { - *pfEmpty = TRUE; - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to get first row while checking if table is empty"); - - *pfEmpty = FALSE; - -LExit: - ReleaseSceRow(row); - - return hr; -} - -extern "C" HRESULT DAPI SceGetFirstRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - DBCOUNTITEM cRowsObtained = 0; - HROW hRow = DB_NULL_HROW; - HROW *phRow = &hRow; - SCE_ROW *pRow = NULL; - SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - - hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER); - ExitOnFailure(hr, "Failed to reset IRowset position to beginning"); - - hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); - if (DB_S_ENDOFROWSET == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to get next first row"); - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - pRow->hRow = hRow; - pRow->pTableSchema = pTable; - pRow->pIRowset = pTable->pIRowset; - pRow->pIRowset->AddRef(); - - *pRowHandle = reinterpret_cast(pRow); - -LExit: - return hr; -} - -HRESULT DAPI SceGetNextRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - DBCOUNTITEM cRowsObtained = 0; - HROW hRow = DB_NULL_HROW; - HROW *phRow = &hRow; - SCE_ROW *pRow = NULL; - SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - - hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); - if (DB_S_ENDOFROWSET == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to get next first row"); - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - pRow->hRow = hRow; - pRow->pTableSchema = pTable; - pRow->pIRowset = pTable->pIRowset; - pRow->pIRowset->AddRef(); - - *pRowHandle = reinterpret_cast(pRow); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceBeginTransaction( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - if (pDatabaseInternal->fTransactionBadState) - { - // We're in a hosed transaction state - we can't start a new one - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE)); - } - - ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); - - if (1 == pDatabaseInternal->dwTransactionRefcount) - { - hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); - ExitOnFailure(hr, "Failed to start transaction"); - } - -LExit: - if (FAILED(hr)) - { - ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); - } - - return hr; -} - -extern "C" HRESULT DAPI SceCommitTransaction( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - Assert(0 < pDatabaseInternal->dwTransactionRefcount); - - ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); - - if (0 == pDatabaseInternal->dwTransactionRefcount) - { - if (pDatabaseInternal->fRollbackTransaction) - { - hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); - ExitOnFailure(hr, "Failed to abort transaction"); - } - else - { - hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0); - ExitOnFailure(hr, "Failed to commit transaction"); - } - - if (pDatabaseInternal->fPendingChanges) - { - pDatabaseInternal->fPendingChanges = FALSE; - pDatabaseInternal->fChanges = TRUE; - } - - pDatabaseInternal->fRollbackTransaction = FALSE; - } - -LExit: - // If we tried to commit and failed, the caller should subsequently call rollback - if (FAILED(hr)) - { - ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); - } - - return hr; -} - -extern "C" HRESULT DAPI SceRollbackTransaction( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - Assert(0 < pDatabaseInternal->dwTransactionRefcount); - - ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); - - if (0 == pDatabaseInternal->dwTransactionRefcount) - { - hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); - ExitOnFailure(hr, "Failed to abort transaction"); - pDatabaseInternal->fPendingChanges = FALSE; - - pDatabaseInternal->fRollbackTransaction = FALSE; - } - else - { - pDatabaseInternal->fRollbackTransaction = TRUE; - } - -LExit: - // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?) - // but mark the database as bad so the user gets an error if they try to start a new transaction. - if (FAILED(hr)) - { - TraceError(hr, "Failed to rollback transaction"); - pDatabaseInternal->fTransactionBadState = TRUE; - } - - return hr; -} - -extern "C" HRESULT DAPI SceDeleteRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(*pRowHandle); - IRowsetChange *pIRowsetChange = NULL; - DBROWSTATUS rowStatus = DBROWSTATUS_S_OK; - - hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); - ExitOnFailure(hr, "Failed to get IRowsetChange interface"); - - hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus); - ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus); - - ReleaseNullSceRow(*pRowHandle); - -LExit: - ReleaseObject(pIRowsetChange); - - return hr; -} - -extern "C" HRESULT DAPI ScePrepareInsert( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = NULL; - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - pRow->hRow = DB_NULL_HROW; - pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - pRow->pIRowset = pRow->pTableSchema->pIRowset; - pRow->pIRowset->AddRef(); - pRow->fInserting = TRUE; - - *pRowHandle = reinterpret_cast(pRow); - pRow = NULL; - -LExit: - ReleaseSceRow(pRow); - - return hr; -} - -extern "C" HRESULT DAPI SceFinishUpdate( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - IAccessor *pIAccessor = NULL; - IRowsetChange *pIRowsetChange = NULL; - DBBINDSTATUS *rgBindStatus = NULL; - HACCESSOR hAccessor = DB_NULL_HACCESSOR; - HROW hRow = DB_NULL_HROW; - - hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - -// This can be used when stepping through the debugger to see bind failures -#ifdef DEBUG - if (0 < pRow->dwBindingIndex) - { - hr = MemEnsureArraySize(reinterpret_cast(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0); - ExitOnFailure(hr, "Failed to ensure binding status array size"); - } -#endif - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); - ExitOnFailure(hr, "Failed to get IRowsetChange interface"); - - if (pRow->fInserting) - { - hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow); - ExitOnFailure(hr, "Failed to insert new row"); - - pRow->hRow = hRow; - ReleaseNullObject(pRow->pIRowset); - pRow->pIRowset = pRow->pTableSchema->pIRowset; - pRow->pIRowset->AddRef(); - } - else - { - hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData); - ExitOnFailure(hr, "Failed to update existing row"); - } - - if (0 < pRow->pDatabaseInternal->dwTransactionRefcount) - { - pRow->pDatabaseInternal->fPendingChanges = TRUE; - } - else - { - pRow->pDatabaseInternal->fChanges = TRUE; - } - -LExit: - if (DB_NULL_HACCESSOR != hAccessor) - { - pIAccessor->ReleaseAccessor(hAccessor, NULL); - } - ReleaseMem(rgBindStatus); - ReleaseObject(pIAccessor); - ReleaseObject(pIRowsetChange); - - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as binary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD dwValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as binary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD64 qwValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as qword"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const BOOL fValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - short int sValue = fValue ? 0xFFFF : 0x0000; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as binary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_z_opt LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR)); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnNull( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as empty value"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const SYSTEMTIME *pst - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - DBTIMESTAMP dbTimeStamp = { }; - - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - dbTimeStamp.year = pst->wYear; - dbTimeStamp.month = pst->wMonth; - dbTimeStamp.day = pst->wDay; - dbTimeStamp.hour = pst->wHour; - dbTimeStamp.minute = pst->wMinute; - dbTimeStamp.second = pst->wSecond; - // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second, - // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds - // are involved (even when rounded the way SQL CE returns them). - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbBuffer, - __inout SIZE_T *pcbBuffer - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get binary data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out DWORD *pdwValue - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast(pdwValue)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get dword data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __in DWORD64 *pqwValue - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast(pqwValue)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get qword data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out BOOL *pfValue - ) -{ - HRESULT hr = S_OK; - short int sValue = 0; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast(&sValue)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get data out of column"); - - if (sValue == 0x0000) - { - *pfValue = FALSE; - } - else - { - *pfValue = TRUE; - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_z LPWSTR *psczValue - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - SIZE_T cbSize = 0; - - hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast(psczValue), &cbSize); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get string data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out SYSTEMTIME *pst - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - DBTIMESTAMP dbTimeStamp = { }; - - hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast(&dbTimeStamp)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get string data out of column"); - - pst->wYear = dbTimeStamp.year; - pst->wMonth = dbTimeStamp.month; - pst->wDay = dbTimeStamp.day; - pst->wHour = dbTimeStamp.hour; - pst->wMinute = dbTimeStamp.minute; - pst->wSecond = dbTimeStamp.second; - -LExit: - return hr; -} - -extern "C" void DAPI SceCloseTable( - __in SCE_TABLE_SCHEMA *pTable - ) -{ - ReleaseNullObject(pTable->pIRowsetChange); - ReleaseNullObject(pTable->pIRowset); -} - -extern "C" BOOL DAPI SceDatabaseChanged( - __in SCE_DATABASE *pDatabase - ) -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - return pDatabaseInternal->fChanges; -} - -void DAPI SceResetDatabaseChanged( - __in SCE_DATABASE *pDatabase - ) -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - pDatabaseInternal->fChanges = FALSE; -} - -extern "C" HRESULT DAPI SceCloseDatabase( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - - ReleaseDatabase(pDatabase); - - return hr; -} - -extern "C" HRESULT DAPI SceBeginQuery( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __in DWORD dwIndex, - __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_QUERY *psq = static_cast(MemAlloc(sizeof(SCE_QUERY), TRUE)); - ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query"); - - psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]); - psq->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns); - - psq->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query"); - - *psqhHandle = static_cast(psq); - psq = NULL; - -LExit: - ReleaseSceQuery(psq); - - return hr; -} - -HRESULT DAPI SceSetQueryColumnBinary( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnDword( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD dwValue - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as dword"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnQword( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD64 qwValue - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as qword"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnBool( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const BOOL fValue - ) -{ - HRESULT hr = S_OK; - short int sValue = fValue ? 1 : 0; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as boolean"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnString( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_z_opt LPCWSTR wzString - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR)); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as string"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnSystemTime( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const SYSTEMTIME *pst - ) -{ - HRESULT hr = S_OK; - DBTIMESTAMP dbTimeStamp = { }; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - dbTimeStamp.year = pst->wYear; - dbTimeStamp.month = pst->wMonth; - dbTimeStamp.day = pst->wDay; - dbTimeStamp.hour = pst->wHour; - dbTimeStamp.minute = pst->wMinute; - dbTimeStamp.second = pst->wSecond; - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnEmpty( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as empty value"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceRunQueryExact( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - SCE_QUERY_RESULTS *pQueryResults = NULL; - - hr = RunQuery(FALSE, *psqhHandle, &pQueryResults); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to run query exact"); - - hr = SceGetNextResultRow(reinterpret_cast(pQueryResults), pRowHandle); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get next row out of results"); - -LExit: - ReleaseNullSceQuery(*psqhHandle); - ReleaseSceQueryResults(pQueryResults); - - return hr; -} - -extern "C" HRESULT DAPI SceRunQueryRange( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle - ) -{ - HRESULT hr = S_OK; - SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast(psqrhHandle); - - hr = RunQuery(TRUE, *psqhHandle, ppQueryResults); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to run query for range"); - -LExit: - ReleaseNullSceQuery(*psqhHandle); - - return hr; -} - -extern "C" HRESULT DAPI SceGetNextResultRow( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - HROW hRow = DB_NULL_HROW; - HROW *phRow = &hRow; - DBCOUNTITEM cRowsObtained = 0; - SCE_ROW *pRow = NULL; - SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); - - Assert(pRowHandle && (*pRowHandle == NULL)); - - hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); - if (DB_S_ENDOFROWSET == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to get next first row"); - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pQueryResults->pDatabaseInternal); - pRow->hRow = hRow; - pRow->pTableSchema = pQueryResults->pTableSchema; - pRow->pIRowset = pQueryResults->pIRowset; - pRow->pIRowset->AddRef(); - - *pRowHandle = reinterpret_cast(pRow); - pRow = NULL; - hRow = DB_NULL_HROW; - -LExit: - if (DB_NULL_HROW != hRow) - { - pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); - } - ReleaseSceRow(pRow); - - return hr; -} - -extern "C" void DAPI SceFreeRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle - ) -{ - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - if (DB_NULL_HROW != pRow->hRow) - { - pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL); - } - ReleaseObject(pRow->pIRowset); - ReleaseMem(pRow->rgBinding); - ReleaseMem(pRow->pbData); - ReleaseMem(pRow); -} - -void DAPI SceFreeQuery( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle - ) -{ - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - ReleaseMem(pQuery->rgBinding); - ReleaseMem(pQuery->pbData); - ReleaseMem(pQuery); -} - -void DAPI SceFreeQueryResults( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle - ) -{ - SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); - - ReleaseObject(pQueryResults->pIRowset); - ReleaseMem(pQueryResults); -} - -// internal function definitions -static HRESULT CreateSqlCe( - __in_z_opt LPCWSTR wzSqlCeDllPath, - __out IDBInitialize **ppIDBInitialize, - __out_opt HMODULE *phSqlCeDll - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - LPWSTR sczDirectory = NULL; - LPWSTR sczDllFullPath = NULL; - - if (NULL == wzSqlCeDllPath) - { - hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast(ppIDBInitialize)); - ExitOnFailure(hr, "Failed to get IDBInitialize interface"); - } - else - { - // First try loading DLL from the path of our EXE - hr = PathForCurrentProcess(&sczPath, NULL); - ExitOnFailure(hr, "Failed to get path for current process"); - - hr = PathGetDirectory(sczPath, &sczDirectory); - ExitOnFailure(hr, "Failed to get directory of current process"); - - hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); - ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); - - *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); - - // If that failed, fallback to loading from current path - if (NULL == *phSqlCeDll) - { - hr = DirGetCurrent(&sczDirectory); - ExitOnFailure(hr, "Failed to get current directory"); - - hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); - ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); - - *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); - ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath); - } - - HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**); - pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject"); - - IClassFactory* pFactory = NULL; - hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory); - ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath); - ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL"); - - hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize); - pFactory->Release(); - } - -LExit: - ReleaseStr(sczPath); - ReleaseStr(sczDirectory); - ReleaseStr(sczDllFullPath); - - return hr; -} - -static HRESULT RunQuery( - __in BOOL fRange, - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, - __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults - ) -{ - HRESULT hr = S_OK; - DBID tableID = { }; - DBID indexID = { }; - IAccessor *pIAccessor = NULL; - IRowsetIndex *pIRowsetIndex = NULL; - IRowset *pIRowset = NULL; - HACCESSOR hAccessor = DB_NULL_HACCESSOR; - SCE_QUERY *pQuery = reinterpret_cast(psqhHandle); - SCE_QUERY_RESULTS *pQueryResults = NULL; - DBPROPSET rgdbpIndexPropSet[1]; - DBPROP rgdbpIndexProp[1]; - - rgdbpIndexPropSet[0].cProperties = 1; - rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET; - rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; - - rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex; - rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpIndexProp[0].colid = DB_NULLID; - rgdbpIndexProp[0].vValue.vt = VT_BOOL; - rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE; - - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pQuery->pTableSchema->wzName); - - indexID.eKind = DBKIND_NAME; - indexID.uName.pwszName = const_cast(pQuery->pIndexSchema->wzName); - - hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex); - ExitOnFailure(hr, "Failed to open IRowsetIndex"); - - hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast(&pIRowset)); - ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex"); - - hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL); - ExitOnFailure(hr, "Failed to create accessor"); - - if (!fRange) - { - hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ); - if (DB_E_NOTFOUND == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to seek to record"); - } - else - { - // If ALL columns in the index were specified, do a full key match - if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns) - { - hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH); - } - else - { - // Otherwise, just match the specified keys. - // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it - // So instead, we set the start and end to the same partial key, and then allow inclusive matching - // This appears to accomplish the same thing - hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0); - } - if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to set range to find records"); - } - - pQueryResults = reinterpret_cast(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE)); - ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct"); - - pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal; - pQueryResults->pTableSchema = pQuery->pTableSchema; - pQueryResults->pIRowset = pIRowset; - pIRowset = NULL; - - *ppQueryResults = pQueryResults; - pQueryResults = NULL; - -LExit: - if (DB_NULL_HACCESSOR != hAccessor) - { - pIAccessor->ReleaseAccessor(hAccessor, NULL); - } - ReleaseObject(pIAccessor); - ReleaseObject(pIRowset); - ReleaseObject(pIRowsetIndex); - ReleaseMem(pQueryResults); - ReleaseSceQueryResults(pQueryResults); - - return hr; -} - -static HRESULT FillOutColumnDescFromSchema( - __in const SCE_COLUMN_SCHEMA *pColumnSchema, - __out DBCOLUMNDESC *pColumnDesc - ) -{ - HRESULT hr = S_OK; - DWORD dwColumnProperties = 0; - DWORD dwColumnPropertyIndex = 0; - BOOL fFixedSize = FALSE; - - pColumnDesc->dbcid.eKind = DBKIND_NAME; - pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName; - pColumnDesc->wType = pColumnSchema->dbtColumnType; - pColumnDesc->ulColumnSize = pColumnSchema->dwLength; - if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType)) - { - fFixedSize = FALSE; - } - else - { - fFixedSize = TRUE; - } - - dwColumnProperties = 1; - if (pColumnSchema->fAutoIncrement) - { - ++dwColumnProperties; - } - if (!pColumnSchema->fNullable) - { - ++dwColumnProperties; - } - - if (0 < dwColumnProperties) - { - pColumnDesc->cPropertySets = 1; - pColumnDesc->rgPropertySets = reinterpret_cast(MemAlloc(sizeof(DBPROPSET), TRUE)); - ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters"); - - pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties; - pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN; - pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE)); - - dwColumnPropertyIndex = 0; - if (pColumnSchema->fAutoIncrement) - { - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE; - ++dwColumnPropertyIndex; - } - if (!pColumnSchema->fNullable) - { - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE; - ++dwColumnPropertyIndex; - } - - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE; - ++dwColumnPropertyIndex; - } - -LExit: - return hr; -} - -static HRESULT EnsureSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pdsSchema - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - BOOL fInTransaction = FALSE; - BOOL fSchemaNeedsSetup = TRUE; - DBID tableID = { }; - DBID indexID = { }; - DBPROPSET rgdbpIndexPropSet[1]; - DBPROPSET rgdbpRowSetPropSet[1]; - DBPROP rgdbpIndexProp[1]; - DBPROP rgdbpRowSetProp[1]; - DBCOLUMNDESC *rgColumnDescriptions = NULL; - DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL; - DWORD cIndexColumnDescriptions = 0; - DWORD dwTableColumnIndex = 0; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - ITableDefinition *pTableDefinition = NULL; - IIndexDefinition *pIndexDefinition = NULL; - IRowsetIndex *pIRowsetIndex = NULL; - - rgdbpRowSetPropSet[0].cProperties = 1; - rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; - rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; - - rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; - rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpRowSetProp[0].colid = DB_NULLID; - rgdbpRowSetProp[0].vValue.vt = VT_BOOL; - rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; - - rgdbpIndexPropSet[0].cProperties = 1; - rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX; - rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; - - rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS; - rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpIndexProp[0].colid = DB_NULLID; - rgdbpIndexProp[0].vValue.vt = VT_I4; - rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL; - - hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast(&pTableDefinition)); - ExitOnFailure(hr, "Failed to get ITableDefinition for table creation"); - - hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast(&pIndexDefinition)); - ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation"); - - hr = SceBeginTransaction(pDatabase); - ExitOnFailure(hr, "Failed to start transaction for ensuring schema"); - fInTransaction = TRUE; - - for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); - - // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist - rgColumnDescriptions = static_cast(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE)); - ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table"); - - for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) - { - hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i); - ExitOnFailure(hr, "Failed to fill out column description from schema"); - } - - // First try to open the table - or if it doesn't exist, create it - hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); - if (DB_E_NOTABLE == hr) - { - // The table doesn't exist, so let's create it - hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL); - ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName); - } - else - { - ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName); - - // Close any rowset we opened - ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset); - - // If it does exist, make sure all columns are in the table - // Only nullable columns can be added to an existing table - for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i) - { - if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable) - hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL); - if (DB_E_DUPLICATECOLUMNID == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName); - } - } - -#pragma prefast(push) -#pragma prefast(disable:26010) - hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable); -#pragma prefast(pop) - ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); - - for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) - { - if (NULL != rgColumnDescriptions[i].rgPropertySets) - { - ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties); - ReleaseMem(rgColumnDescriptions[i].rgPropertySets); - } - } - - ReleaseNullMem(rgColumnDescriptions); - if (0 < pdsSchema->rgTables[dwTable].cIndexes) - { - // Now create indexes for the table - for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex) - { - indexID.eKind = DBKIND_NAME; - indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName; - - // Check if the index exists - hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex); - if (SUCCEEDED(hr)) - { - // TODO: If one with the same name exists, check if the schema actually matches - ReleaseNullObject(pIRowsetIndex); - continue; - } - hr = S_OK; - - hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns); - - rgIndexColumnDescriptions = reinterpret_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions"); - cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns; - - for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex) - { - dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex]; - - rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast(MemAlloc(sizeof(DBID), TRUE)); - rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME; - rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName); - rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC; - } - - hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL); - if (DB_E_DUPLICATEINDEXID == hr) - { - // If the index already exists, no worries - hr = S_OK; - } - ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName); - - for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) - { - ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); - } - - cIndexColumnDescriptions = 0; - ReleaseNullMem(rgIndexColumnDescriptions); - } - } - } - - // Now once all tables have been created, create foreign key relationships - if (fSchemaNeedsSetup) - { - for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); - - // Setup any constraints for the table's columns - hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema); - ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); - } - } - - hr = SceCommitTransaction(pDatabase); - ExitOnFailure(hr, "Failed to commit transaction for ensuring schema"); - fInTransaction = FALSE; - - hr = OpenSchema(pDatabase, pdsSchema); - ExitOnFailure(hr, "Failed to open schema"); - -LExit: - ReleaseObject(pTableDefinition); - ReleaseObject(pIndexDefinition); - ReleaseObject(pIRowsetIndex); - - if (fInTransaction) - { - SceRollbackTransaction(pDatabase); - } - - for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) - { - ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); - } - - ReleaseMem(rgIndexColumnDescriptions); - ReleaseMem(rgColumnDescriptions); - - return hr; -} - -static HRESULT OpenSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pdsSchema - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - DBID tableID = { }; - DBPROPSET rgdbpRowSetPropSet[1]; - DBPROP rgdbpRowSetProp[1]; - - rgdbpRowSetPropSet[0].cProperties = 1; - rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; - rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; - - rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; - rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpRowSetProp[0].colid = DB_NULLID; - rgdbpRowSetProp[0].vValue.vt = VT_BOOL; - rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; - - // Finally, open all tables - for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); - - // And finally, open the table's standard interfaces - hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); - ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName); - - hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowsetChange)); - ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName); - } - -LExit: - return hr; -} - -static HRESULT SetColumnValue( - __in const SCE_TABLE_SCHEMA *pTableSchema, - __in DWORD dwColumnIndex, - __in_bcount_opt(cbSize) const BYTE *pbData, - __in SIZE_T cbSize, - __inout DBBINDING *pBinding, - __inout SIZE_T *pcbOffset, - __inout BYTE **ppbBuffer - ) -{ - HRESULT hr = S_OK; - size_t cbNewOffset = *pcbOffset; - - pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column - pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; - pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; - - pBinding->obLength = cbNewOffset; - - hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset); - ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value"); - - pBinding->obValue = cbNewOffset; - - hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset); - ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize); - - pBinding->obStatus = cbNewOffset; - pBinding->eParamIO = DBPARAMIO_INPUT; - - hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset); - ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value"); - - pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType; - pBinding->cbMaxLen = static_cast(cbSize); - - if (NULL == *ppbBuffer) - { - *ppbBuffer = reinterpret_cast(MemAlloc(cbNewOffset, TRUE)); - ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string"); - } - else - { - *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE)); - ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string"); - } - - *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = static_cast(cbSize); - *pcbOffset += sizeof(DBBYTEOFFSET); - memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize); - *pcbOffset += cbSize; - if (NULL == pbData) - { - *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL; - } - *pcbOffset += sizeof(DBSTATUS); - -LExit: - return hr; -} - -static HRESULT GetColumnValue( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbData, - __out SIZE_T *pcbSize - ) -{ - HRESULT hr = S_OK; - const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; - IAccessor *pIAccessor = NULL; - HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; - HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; - DWORD dwDataSize = 0; - void *pvRawData = NULL; - DBBINDING dbBinding = { }; - DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; - - dbBinding.iOrdinal = dwColumnIndex + 1; - dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; - dbBinding.dwPart = DBPART_LENGTH; - dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; - - pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); - ExitOnFailure(hr, "Failed to get size of data"); - - // For variable-length columns, zero data returned means NULL - if (0 == dwDataSize) - { - ExitFunction1(hr = E_NOTFOUND); - } - - if (NULL != ppbData) - { - dbBinding.dwPart = DBPART_VALUE; - dbBinding.cbMaxLen = dwDataSize; - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - if (DBBINDSTATUS_OK != dbBindStatus) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); - } - - if (DBTYPE_WSTR == dbBinding.wType) - { - hr = StrAlloc(reinterpret_cast(&pvRawData), dwDataSize / sizeof(WCHAR)); - ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex); - } - else - { - pvRawData = MemAlloc(dwDataSize, TRUE); - ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex); - } - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData); - ExitOnFailure(hr, "Failed to read data value"); - - ReleaseMem(*ppbData); - *ppbData = reinterpret_cast(pvRawData); - pvRawData = NULL; - } - - *pcbSize = dwDataSize; - -LExit: - ReleaseMem(pvRawData); - - if (DB_NULL_HACCESSOR != hAccessorLength) - { - pIAccessor->ReleaseAccessor(hAccessorLength, NULL); - } - if (DB_NULL_HACCESSOR != hAccessorValue) - { - pIAccessor->ReleaseAccessor(hAccessorValue, NULL); - } - ReleaseObject(pIAccessor); - - return hr; -} - -static HRESULT GetColumnValueFixed( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __in DWORD cbSize, - __out BYTE *pbData - ) -{ - HRESULT hr = S_OK; - const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; - IAccessor *pIAccessor = NULL; - HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; - HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; - DWORD dwDataSize = 0; - DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; - DBBINDING dbBinding = { }; - - dbBinding.iOrdinal = dwColumnIndex + 1; - dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; - dbBinding.dwPart = DBPART_LENGTH; - dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; - - pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - if (DBBINDSTATUS_OK != dbBindStatus) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value"); - } - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); - ExitOnFailure(hr, "Failed to get size of data"); - - if (0 == dwDataSize) - { - ExitFunction1(hr = E_NOTFOUND); - } - - dbBinding.dwPart = DBPART_VALUE; - dbBinding.cbMaxLen = cbSize; - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - if (DBBINDSTATUS_OK != dbBindStatus) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); - } - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast(pbData)); - ExitOnFailure(hr, "Failed to read data value"); - -LExit: - if (DB_NULL_HACCESSOR != hAccessorLength) - { - pIAccessor->ReleaseAccessor(hAccessorLength, NULL); - } - if (DB_NULL_HACCESSOR != hAccessorValue) - { - pIAccessor->ReleaseAccessor(hAccessorValue, NULL); - } - ReleaseObject(pIAccessor); - - return hr; -} - -static HRESULT EnsureLocalColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema - ) -{ - HRESULT hr = S_OK; - SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; - DBCONSTRAINTDESC dbcdConstraint = { }; - DBID dbConstraintID = { }; - DBID dbLocalColumnID = { }; - ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; - - hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); - ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); - - for (DWORD i = 0; i < pTableSchema->cColumns; ++i) - { - pCurrentColumn = pTableSchema->rgColumns + i; - - // Add a primary key constraint for this column, if one exists - if (pCurrentColumn->fPrimaryKey) - { - // Setup DBID for new constraint - dbConstraintID.eKind = DBKIND_NAME; - dbConstraintID.uName.pwszName = const_cast(L"PrimaryKey"); - dbcdConstraint.pConstraintID = &dbConstraintID; - - dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY; - - dbLocalColumnID.eKind = DBKIND_NAME; - dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); - dbcdConstraint.cColumns = 1; - dbcdConstraint.rgColumnList = &dbLocalColumnID; - - dbcdConstraint.pReferencedTableID = NULL; - dbcdConstraint.cForeignKeyColumns = 0; - dbcdConstraint.rgForeignKeyColumnList = NULL; - dbcdConstraint.pwszConstraintText = NULL; - dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; - dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; - dbcdConstraint.MatchType = DBMATCHTYPE_NONE; - dbcdConstraint.Deferrability = 0; - dbcdConstraint.cReserved = 0; - dbcdConstraint.rgReserved = NULL; - - hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); - if (DB_E_DUPLICATECONSTRAINTID == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName); - } - } - -LExit: - ReleaseObject(pTableDefinitionWithConstraints); - - return hr; -} - -static HRESULT EnsureForeignColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema, - __in SCE_DATABASE_SCHEMA *pDatabaseSchema - ) -{ - HRESULT hr = S_OK; - SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; - DBCONSTRAINTDESC dbcdConstraint = { }; - DBID dbConstraintID = { }; - DBID dbLocalColumnID = { }; - DBID dbForeignTableID = { }; - DBID dbForeignColumnID = { }; - ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; - - hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); - ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); - - for (DWORD i = 0; i < pTableSchema->cColumns; ++i) - { - pCurrentColumn = pTableSchema->rgColumns + i; - - // Add a foreign key constraint for this column, if one exists - if (NULL != pCurrentColumn->wzRelationName) - { - // Setup DBID for new constraint - dbConstraintID.eKind = DBKIND_NAME; - dbConstraintID.uName.pwszName = const_cast(pCurrentColumn->wzRelationName); - dbcdConstraint.pConstraintID = &dbConstraintID; - - dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY; - - dbForeignColumnID.eKind = DBKIND_NAME; - dbForeignColumnID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName); - dbcdConstraint.cColumns = 1; - dbcdConstraint.rgColumnList = &dbForeignColumnID; - - dbForeignTableID.eKind = DBKIND_NAME; - dbForeignTableID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName); - dbcdConstraint.pReferencedTableID = &dbForeignTableID; - - dbLocalColumnID.eKind = DBKIND_NAME; - dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); - dbcdConstraint.cForeignKeyColumns = 1; - dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID; - - dbcdConstraint.pwszConstraintText = NULL; - dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; - dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; - dbcdConstraint.MatchType = DBMATCHTYPE_FULL; - dbcdConstraint.Deferrability = 0; - dbcdConstraint.cReserved = 0; - dbcdConstraint.rgReserved = NULL; - - hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); - if (DB_E_DUPLICATECONSTRAINTID == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName); - } - } - -LExit: - ReleaseObject(pTableDefinitionWithConstraints); - - return hr; -} - -static HRESULT SetSessionProperties( - __in ISessionProperties *pISessionProperties - ) -{ - HRESULT hr = S_OK; - DBPROP rgdbpDataSourceProp[1]; - DBPROPSET rgdbpDataSourcePropSet[1]; - - rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE; - rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[0].vValue.vt = VT_I4; - rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH; - - rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION; - rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; - rgdbpDataSourcePropSet[0].cProperties = 1; - - hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet); - ExitOnFailure(hr, "Failed to set session properties"); - -LExit: - return hr; -} - -static HRESULT GetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __out LPWSTR *psczSchemaType, - __out DWORD *pdwVersion - ) -{ - HRESULT hr = S_OK; - LPWSTR sczSchemaType = NULL; - DWORD dwVersionFound = 0; - SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; - SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; - // Database object with our alternate schema - SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; - SCE_ROW_HANDLE sceRow = NULL; - - hr = OpenSchema(pDatabase, &fullSchema); - ExitOnFailure(hr, "Failed to ensure internal version schema"); - - hr = SceGetFirstRow(&database, 0, &sceRow); - ExitOnFailure(hr, "Failed to get first row in internal version schema table"); - - hr = SceGetColumnString(sceRow, 0, &sczSchemaType); - ExitOnFailure(hr, "Failed to get internal schematype"); - - hr = SceGetColumnDword(sceRow, 1, &dwVersionFound); - ExitOnFailure(hr, "Failed to get internal version"); - - *psczSchemaType = sczSchemaType; - sczSchemaType = NULL; - *pdwVersion = dwVersionFound; - -LExit: - SceCloseTable(&schemaTable); // ignore failure - ReleaseStr(sczSchemaType); - ReleaseSceRow(sceRow); - - return hr; -} - -static HRESULT SetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __in LPCWSTR wzSchemaType, - __in DWORD dwVersion - ) -{ - HRESULT hr = S_OK; - BOOL fInSceTransaction = FALSE; - SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; - SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; - // Database object with our alternate schema - SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; - SCE_ROW_HANDLE sceRow = NULL; - - hr = EnsureSchema(pDatabase, &fullSchema); - ExitOnFailure(hr, "Failed to ensure internal version schema"); - - hr = SceBeginTransaction(&database); - ExitOnFailure(hr, "Failed to begin transaction"); - fInSceTransaction = TRUE; - - hr = SceGetFirstRow(&database, 0, &sceRow); - if (E_NOTFOUND == hr) - { - hr = ScePrepareInsert(&database, 0, &sceRow); - ExitOnFailure(hr, "Failed to insert only row into internal version schema table"); - } - else - { - ExitOnFailure(hr, "Failed to get first row in internal version schema table"); - } - - hr = SceSetColumnString(sceRow, 0, wzSchemaType); - ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType); - - hr = SceSetColumnDword(sceRow, 1, dwVersion); - ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion); - - hr = SceFinishUpdate(sceRow); - ExitOnFailure(hr, "Failed to insert first row in internal version schema table"); - - hr = SceCommitTransaction(&database); - ExitOnFailure(hr, "Failed to commit transaction"); - fInSceTransaction = FALSE; - -LExit: - SceCloseTable(&schemaTable); // ignore failure - ReleaseSceRow(sceRow); - if (fInSceTransaction) - { - SceRollbackTransaction(&database); - } - - return hr; -} - -static void ReleaseDatabase( - SCE_DATABASE *pDatabase - ) -{ - if (NULL != pDatabase) - { - if (NULL != pDatabase->pdsSchema) - { - for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i) - { - SceCloseTable(pDatabase->pdsSchema->rgTables + i); - } - } - - if (NULL != pDatabase->sdbHandle) - { - ReleaseDatabaseInternal(reinterpret_cast(pDatabase->sdbHandle)); - } - } - ReleaseMem(pDatabase); -} - -static void ReleaseDatabaseInternal( - SCE_DATABASE_INTERNAL *pDatabaseInternal - ) -{ - HRESULT hr = S_OK; - - if (NULL != pDatabaseInternal) - { - ReleaseObject(pDatabaseInternal->pITransactionLocal); - ReleaseObject(pDatabaseInternal->pIOpenRowset); - ReleaseObject(pDatabaseInternal->pISessionProperties); - ReleaseObject(pDatabaseInternal->pIDBCreateSession); - ReleaseObject(pDatabaseInternal->pIDBProperties); - - if (NULL != pDatabaseInternal->pIDBInitialize) - { - hr = pDatabaseInternal->pIDBInitialize->Uninitialize(); - if (FAILED(hr)) - { - TraceError(hr, "Failed to call uninitialize on IDBInitialize"); - } - ReleaseObject(pDatabaseInternal->pIDBInitialize); - } - - if (NULL != pDatabaseInternal->hSqlCeDll) - { - if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to free sql ce dll"); - } - } - } - - // If there was a temp file we copied to (for read-only databases), delete it after close - if (NULL != pDatabaseInternal->sczTempDbFile) - { - hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile); - if (FAILED(hr)) - { - TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile); - } - ReleaseStr(pDatabaseInternal->sczTempDbFile); - } - - ReleaseMem(pDatabaseInternal); -} - -#endif // end SKIP_SCE_COMPILE diff --git a/src/dutil/shelutil.cpp b/src/dutil/shelutil.cpp deleted file mode 100644 index 2eb9a52a..00000000 --- a/src/dutil/shelutil.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) -#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) -#define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) -#define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) -#define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__) -#define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__) - -static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; - -static HRESULT GetDesktopShellView( - __in REFIID riid, - __out void **ppv - ); -static HRESULT GetShellDispatchFromView( - __in IShellView *psv, - __in REFIID riid, - __out void **ppv - ); - -/******************************************************************** - ShelFunctionOverride - overrides the shell functions. Typically used - for unit testing. - -*********************************************************************/ -extern "C" void DAPI ShelFunctionOverride( - __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW - ) -{ - vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW; -} - - -/******************************************************************** - ShelExec() - executes a target. - -*******************************************************************/ -extern "C" HRESULT DAPI ShelExec( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd, - __in_opt HWND hwndParent, - __out_opt HANDLE* phProcess - ) -{ - HRESULT hr = S_OK; - SHELLEXECUTEINFOW shExecInfo = {}; - - shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; - shExecInfo.hwnd = hwndParent; - shExecInfo.lpVerb = wzVerb; - shExecInfo.lpFile = wzTargetPath; - shExecInfo.lpParameters = wzParameters; - shExecInfo.lpDirectory = wzWorkingDirectory; - shExecInfo.nShow = nShowCmd; - - if (!vpfnShellExecuteExW(&shExecInfo)) - { - ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); - } - - if (phProcess) - { - *phProcess = shExecInfo.hProcess; - shExecInfo.hProcess = NULL; - } - -LExit: - ReleaseHandle(shExecInfo.hProcess); - - return hr; -} - - -/******************************************************************** - ShelExecUnelevated() - executes a target unelevated. - -*******************************************************************/ -extern "C" HRESULT DAPI ShelExecUnelevated( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd - ) -{ - HRESULT hr = S_OK; - BSTR bstrTargetPath = NULL; - VARIANT vtParameters = { }; - VARIANT vtVerb = { }; - VARIANT vtWorkingDirectory = { }; - VARIANT vtShow = { }; - IShellView* psv = NULL; - IShellDispatch2* psd = NULL; - - bstrTargetPath = ::SysAllocString(wzTargetPath); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); - - if (wzParameters && *wzParameters) - { - vtParameters.vt = VT_BSTR; - vtParameters.bstrVal = ::SysAllocString(wzParameters); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); - } - - if (wzVerb && *wzVerb) - { - vtVerb.vt = VT_BSTR; - vtVerb.bstrVal = ::SysAllocString(wzVerb); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); - } - - if (wzWorkingDirectory && *wzWorkingDirectory) - { - vtWorkingDirectory.vt = VT_BSTR; - vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); - } - - vtShow.vt = VT_INT; - vtShow.intVal = nShowCmd; - - hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); - ShelExitOnFailure(hr, "Failed to get desktop shell view."); - - hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); - ShelExitOnFailure(hr, "Failed to get shell dispatch from view."); - - hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); - } - ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); - -LExit: - ReleaseObject(psd); - ReleaseObject(psv); - ReleaseBSTR(vtWorkingDirectory.bstrVal); - ReleaseBSTR(vtVerb.bstrVal); - ReleaseBSTR(vtParameters.bstrVal); - ReleaseBSTR(bstrTargetPath); - - return hr; -} - - -/******************************************************************** - ShelGetFolder() - gets a folder by CSIDL. - -*******************************************************************/ -extern "C" HRESULT DAPI ShelGetFolder( - __out_z LPWSTR* psczFolderPath, - __in int csidlFolder - ) -{ - HRESULT hr = S_OK; - WCHAR wzPath[MAX_PATH]; - - hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); - ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); - - hr = StrAllocString(psczFolderPath, wzPath, 0); - ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); - - hr = PathBackslashTerminate(psczFolderPath); - ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); - -LExit: - return hr; -} - - -/******************************************************************** - ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. - - Note: return E_NOTIMPL if called on pre-Vista operating systems. -*******************************************************************/ -#ifndef REFKNOWNFOLDERID -#define REFKNOWNFOLDERID REFGUID -#endif - -#ifndef KF_FLAG_CREATE -#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition -#endif - -EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( - REFKNOWNFOLDERID rfid, - DWORD dwFlags, - HANDLE hToken, - PWSTR *ppszPath - ); - -extern "C" HRESULT DAPI ShelGetKnownFolder( - __out_z LPWSTR* psczFolderPath, - __in REFKNOWNFOLDERID rfidFolder - ) -{ - HRESULT hr = S_OK; - HMODULE hShell32Dll = NULL; - PFN_SHGetKnownFolderPath pfn = NULL; - LPWSTR pwzPath = NULL; - - hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll); - if (E_MODNOTFOUND == hr) - { - TraceError(hr, "Failed to load shell32.dll"); - ExitFunction1(hr = E_NOTIMPL); - } - ShelExitOnFailure(hr, "Failed to load shell32.dll."); - - pfn = reinterpret_cast(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); - ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); - - hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); - ShelExitOnFailure(hr, "Failed to get known folder path."); - - hr = StrAllocString(psczFolderPath, pwzPath, 0); - ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); - - hr = PathBackslashTerminate(psczFolderPath); - ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); - -LExit: - if (pwzPath) - { - ::CoTaskMemFree(pwzPath); - } - - if (hShell32Dll) - { - ::FreeLibrary(hShell32Dll); - } - - return hr; -} - - -// Internal functions. - -static HRESULT GetDesktopShellView( - __in REFIID riid, - __out void **ppv - ) -{ - HRESULT hr = S_OK; - IShellWindows* psw = NULL; - HWND hwnd = NULL; - IDispatch* pdisp = NULL; - VARIANT vEmpty = {}; // VT_EMPTY - IShellBrowser* psb = NULL; - IShellFolder* psf = NULL; - IShellView* psv = NULL; - - // use the shell view for the desktop using the shell windows automation to find the - // desktop web browser and then grabs its view - // returns IShellView, IFolderView and related interfaces - hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); - ShelExitOnFailure(hr, "Failed to get shell view."); - - hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); - if (S_OK == hr) - { - hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); - ShelExitOnFailure(hr, "Failed to get desktop window."); - - hr = psb->QueryActiveShellView(&psv); - ShelExitOnFailure(hr, "Failed to get active shell view."); - - hr = psv->QueryInterface(riid, ppv); - ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); - } - else if (S_FALSE == hr) - { - //Windows XP - hr = SHGetDesktopFolder(&psf); - ShelExitOnFailure(hr, "Failed to get desktop folder."); - - hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); - ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); - } - else - { - ShelExitOnFailure(hr, "Failed to get desktop window."); - } - -LExit: - ReleaseObject(psv); - ReleaseObject(psb); - ReleaseObject(psf); - ReleaseObject(pdisp); - ReleaseObject(psw); - - return hr; -} - -static HRESULT GetShellDispatchFromView( - __in IShellView *psv, - __in REFIID riid, - __out void **ppv - ) -{ - HRESULT hr = S_OK; - IDispatch *pdispBackground = NULL; - IShellFolderViewDual *psfvd = NULL; - IDispatch *pdisp = NULL; - - // From a shell view object, gets its automation interface and from that get the shell - // application object that implements IShellDispatch2 and related interfaces. - hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); - ShelExitOnFailure(hr, "Failed to get the automation interface for shell."); - - hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); - ShelExitOnFailure(hr, "Failed to get shell folder view dual."); - - hr = psfvd->get_Application(&pdisp); - ShelExitOnFailure(hr, "Failed to application object."); - - hr = pdisp->QueryInterface(riid, ppv); - ShelExitOnFailure(hr, "Failed to get IShellDispatch2."); - -LExit: - ReleaseObject(pdisp); - ReleaseObject(psfvd); - ReleaseObject(pdispBackground); - - return hr; -} diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp deleted file mode 100644 index 782c7088..00000000 --- a/src/dutil/sqlutil.cpp +++ /dev/null @@ -1,1002 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// okay, this may look a little weird, but sqlutil.h cannot be in the -// pre-compiled header because we need to #define these things so the -// correct GUID's get pulled into this object file -#include -#define DBINITCONSTANTS -#include "sqlutil.h" - - -//Please note that only SQL native client 11 has TLS1.2 support -#define _SQLNCLI_OLEDB_DEPRECATE_WARNING - -#if !defined(SQLNCLI_VER) -#define SQLNCLI_VER 1100 -#endif - -#if SQLNCLI_VER >= 1100 -#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) -#define SQLNCLI_CLSID CLSID_SQLNCLI11 -#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) -extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } }; -#endif // SQLNCLI_VER >= 1100 - -// Exit macros -#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) -#define SqlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) -#define SqlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) -#define SqlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) -#define SqlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SQLUTIL, e, x, s, __VA_ARGS__) -#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__) - -// private prototypes -static HRESULT InitializeDatabaseConnection( - __in REFCLSID rclsid, - __in_z LPCSTR szFriendlyClsidName, - __in DBPROPSET rgdbpsetInit[], - __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, - __out IDBCreateSession** ppidbSession - ); -HRESULT DumpErrorRecords(); -static HRESULT FileSpecToString( - __in const SQL_FILESPEC* psf, - __out LPWSTR* ppwz - ); -static HRESULT EscapeSqlIdentifier( - __in_z LPCWSTR wzDatabase, - __deref_out_z LPWSTR* ppwz - ); - - -/******************************************************************** - SqlConnectDatabase - establishes a connection to a database - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlConnectDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out IDBCreateSession** ppidbSession - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession); - - HRESULT hr = S_OK; - LPWSTR pwzServerInstance = NULL; - DBPROP rgdbpInit[4] = { }; - DBPROPSET rgdbpsetInit[1] = { }; - ULONG cProperties = 0; - - // if there is an instance - if (wzInstance && *wzInstance) - { - hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance); - } - else - { - hr = StrAllocString(&pwzServerInstance, wzServer, 0); - } - SqlExitOnFailure(hr, "failed to allocate memory for the server instance"); - - // server[\instance] - rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance); - ++cProperties; - - // database - rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase); - ++cProperties; - - if (fIntegratedAuth) - { - // username - rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication - ++cProperties; - } - else - { - // username - rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser); - ++cProperties; - - // password - rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword); - ++cProperties; - } - - // put the properties into a set - rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT; - rgdbpsetInit[0].rgProperties = rgdbpInit; - rgdbpsetInit[0].cProperties = cProperties; - - // obtain access to the SQL Native Client provider - hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); - if (FAILED(hr)) - { - SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB..."); - - // try OLE DB but if that fails return original error failure - HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); - if (FAILED(hr2)) - { - SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up."); - } - else - { - hr = S_OK; - } - } - -LExit: - for (; 0 < cProperties; cProperties--) - { - ::VariantClear(&rgdbpInit[cProperties - 1].vValue); - } - - ReleaseStr(pwzServerInstance); - - return hr; -} - - -/******************************************************************** - SqlStartTransaction - Starts a new transaction that must be ended - -*********************************************************************/ -extern "C" HRESULT DAPI SqlStartTransaction( - __in IDBCreateSession* pidbSession, - __out IDBCreateCommand** ppidbCommand, - __out ITransaction** ppit - ) -{ - Assert(pidbSession && ppit); - - HRESULT hr = S_OK; - - hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand); - SqlExitOnFailure(hr, "unable to create command from session"); - - hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit); - SqlExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal"); - - hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); - -LExit: - - return hr; -} - -/******************************************************************** - SqlEndTransaction - Ends the transaction - - NOTE: if fCommit, will commit the transaction, otherwise rolls back -*********************************************************************/ -extern "C" HRESULT DAPI SqlEndTransaction( - __in ITransaction* pit, - __in BOOL fCommit - ) -{ - Assert(pit); - - HRESULT hr = S_OK; - - if (fCommit) - { - hr = pit->Commit(FALSE, XACTTC_SYNC, 0); - SqlExitOnFailure(hr, "commit of transaction failed"); - } - else - { - hr = pit->Abort(NULL, FALSE, FALSE); - SqlExitOnFailure(hr, "abort of transaction failed"); - } - -LExit: - - return hr; -} - - -/******************************************************************** - SqlDatabaseExists - determines if database exists - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored - returns S_OK if database exist - returns S_FALSE if database does not exist - returns E_* on error -********************************************************************/ -extern "C" HRESULT DAPI SqlDatabaseExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); - - hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionDatabaseExists - determines if database exists - - NOTE: pidbSession must be connected to master database - returns S_OK if database exist - returns S_FALSE if database does not exist - returns E_* on error -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionDatabaseExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - - LPWSTR pwzQuery = NULL; - IRowset* pirs = NULL; - - DBCOUNTITEM cRows = 0; - HROW rghRows[1]; - HROW* prow = rghRows; - - // - // query to see if the database exists - // - hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase); - SqlExitOnFailure(hr, "failed to allocate query string to ensure database exists"); - - hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to get database list from 'master' database"); - Assert(pirs); - - // - // check to see if the database was returned - // - hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow); - SqlExitOnFailure(hr, "failed to get row with database name"); - - // succeeded but no database - if ((DB_S_ENDOFROWSET == hr) || (0 == cRows)) - { - hr = S_FALSE; - } - -LExit: - ReleaseObject(pirs); - ReleaseStr(pwzQuery); - - return hr; -} - - -/******************************************************************** - SqlDatabaseEnsureExists - creates a database if it does not exist - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlDatabaseEnsureExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - // - // connect to the master database to create the new database - // - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); - - hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); - - Assert(S_OK == hr); -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionDatabaseEnsureExists - creates a database if it does not exist - - NOTE: pidbSession must be connected to the master database -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - - hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); - - if (S_FALSE == hr) - { - hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); - } - // else database already exists, return S_FALSE - - Assert(S_OK == hr); -LExit: - - return hr; -} - - -/******************************************************************** - SqlCreateDatabase - creates a database on the server - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlCreateDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - // - // connect to the master database to create the new database - // - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); - - hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); - - Assert(S_OK == hr); -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionCreateDatabase - creates a database on the server - - NOTE: pidbSession must be connected to the master database -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionCreateDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - HRESULT hr = S_OK; - LPWSTR pwzDbFile = NULL; - LPWSTR pwzLogFile = NULL; - LPWSTR pwzQuery = NULL; - LPWSTR pwzDatabaseEscaped = NULL; - - if (psfDatabase) - { - hr = FileSpecToString(psfDatabase, &pwzDbFile); - SqlExitOnFailure(hr, "failed to convert db filespec to string"); - } - - if (psfLog) - { - hr = FileSpecToString(psfLog, &pwzLogFile); - SqlExitOnFailure(hr, "failed to convert log filespec to string"); - } - - hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); - SqlExitOnFailure(hr, "failed to escape database string"); - - 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""); - SqlExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped); - - hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery); - -LExit: - ReleaseStr(pwzQuery); - ReleaseStr(pwzLogFile); - ReleaseStr(pwzDbFile); - ReleaseStr(pwzDatabaseEscaped); - - return hr; -} - - -/******************************************************************** - SqlDropDatabase - removes a database from a server if it exists - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlDropDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - // - // connect to the master database to search for wzDatabase - // - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "Failed to connect to 'master' database"); - - hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription); - -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionDropDatabase - removes a database from a server if it exists - - NOTE: pidbSession must be connected to the master database -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionDropDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - LPWSTR pwzQuery = NULL; - LPWSTR pwzDatabaseEscaped = NULL; - - hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); - - hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); - SqlExitOnFailure(hr, "failed to escape database string"); - - if (S_OK == hr) - { - hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped); - SqlExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped); - - hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); - SqlExitOnFailure(hr, "Failed to drop database"); - } - -LExit: - ReleaseStr(pwzQuery); - ReleaseStr(pwzDatabaseEscaped); - - return hr; -} - - -/******************************************************************** - SqlSessionExecuteQuery - executes a query and returns the results if desired - - NOTE: ppirs and pcRoes and pbstrErrorDescription are optional -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionExecuteQuery( - __in IDBCreateSession* pidbSession, - __in __sql_command LPCWSTR wzSql, - __out_opt IRowset** ppirs, - __out_opt DBROWCOUNT* pcRows, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession); - - HRESULT hr = S_OK; - IDBCreateCommand* pidbCommand = NULL; - ICommandText* picmdText = NULL; - ICommand* picmd = NULL; - DBROWCOUNT cRows = 0; - - if (pcRows) - { - *pcRows = NULL; - } - - // - // create the command - // - hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand); - SqlExitOnFailure(hr, "failed to create database session"); - hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); - SqlExitOnFailure(hr, "failed to create command to execute session"); - - // - // set the sql text into the command - // - hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); - SqlExitOnFailure(hr, "failed to get command text object for command"); - hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); - SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); - - // - // execute the command - // - hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); - SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); - - if (DB_S_ERRORSOCCURRED == hr) - { - hr = E_FAIL; - } - - if (pcRows) - { - *pcRows = cRows; - } - -LExit: - - if (FAILED(hr) && picmd && pbstrErrorDescription) - { - HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English - if (FAILED(hrGetErrors)) - { - ReleaseBSTR(*pbstrErrorDescription); - } - } - - ReleaseObject(picmd); - ReleaseObject(picmdText); - ReleaseObject(pidbCommand); - - return hr; -} - - -/******************************************************************** - SqlCommandExecuteQuery - executes a SQL command and returns the results if desired - - NOTE: ppirs and pcRoes are optional -********************************************************************/ -extern "C" HRESULT DAPI SqlCommandExecuteQuery( - __in IDBCreateCommand* pidbCommand, - __in __sql_command LPCWSTR wzSql, - __out IRowset** ppirs, - __out DBROWCOUNT* pcRows - ) -{ - Assert(pidbCommand); - - HRESULT hr = S_OK; - ICommandText* picmdText = NULL; - ICommand* picmd = NULL; - DBROWCOUNT cRows = 0; - - if (pcRows) - { - *pcRows = NULL; - } - - // - // create the command - // - hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); - SqlExitOnFailure(hr, "failed to create command to execute session"); - - // - // set the sql text into the command - // - hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); - SqlExitOnFailure(hr, "failed to get command text object for command"); - hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); - SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); - - // - // execute the command - // - hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); - SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); - - if (DB_S_ERRORSOCCURRED == hr) - { - hr = E_FAIL; - } - - if (pcRows) - { - *pcRows = cRows; - } - -LExit: - ReleaseObject(picmd); - ReleaseObject(picmdText); - - return hr; -} - - -/******************************************************************** - SqlGetErrorInfo - gets error information from the last SQL function call - - NOTE: pbstrErrorSource and pbstrErrorDescription are optional -********************************************************************/ -extern "C" HRESULT DAPI SqlGetErrorInfo( - __in IUnknown* pObjectWithError, - __in REFIID IID_InterfaceWithError, - __in DWORD dwLocaleId, - __out_opt BSTR* pbstrErrorSource, - __out_opt BSTR* pbstrErrorDescription - ) -{ - HRESULT hr = S_OK; - Assert(pObjectWithError); - - // interfaces needed to extract error information out - ISupportErrorInfo* pISupportErrorInfo = NULL; - IErrorInfo* pIErrorInfoAll = NULL; - IErrorRecords* pIErrorRecords = NULL; - IErrorInfo* pIErrorInfoRecord = NULL; - - // only ask for error information if the interface supports it. - hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo); - SqlExitOnFailure(hr, "No error information was found for object."); - - hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError); - SqlExitOnFailure(hr, "InterfaceWithError is not supported for object with error"); - - // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway - hr = ::GetErrorInfo(0, &pIErrorInfoAll); - SqlExitOnFailure(hr, "failed to get error info"); - - if (S_OK == hr && pIErrorInfoAll) - { - // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records - hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); - if (SUCCEEDED(hr)) - { - ULONG cErrors = 0; - pIErrorRecords->GetRecordCount(&cErrors); - - // get the error information for each record - for (ULONG i = 0; i < cErrors; ++i) - { - hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord); - if (SUCCEEDED(hr)) - { - if (pbstrErrorSource) - { - pIErrorInfoRecord->GetSource(pbstrErrorSource); - } - if (pbstrErrorDescription) - { - pIErrorInfoRecord->GetDescription(pbstrErrorDescription); - } - - ReleaseNullObject(pIErrorInfoRecord); - - break; // TODO: return more than one error in the future! - } - } - - ReleaseNullObject(pIErrorRecords); - } - else // we have a simple error record - { - if (pbstrErrorSource) - { - pIErrorInfoAll->GetSource(pbstrErrorSource); - } - if (pbstrErrorDescription) - { - pIErrorInfoAll->GetDescription(pbstrErrorDescription); - } - } - } - else - { - hr = E_NOMOREITEMS; - } - -LExit: - ReleaseObject(pIErrorInfoRecord); - ReleaseObject(pIErrorRecords); - ReleaseObject(pIErrorInfoAll); - ReleaseObject(pISupportErrorInfo); - - return hr; -} - - -// -// private -// - -static HRESULT InitializeDatabaseConnection( - __in REFCLSID rclsid, - __in_z LPCSTR szFriendlyClsidName, - __in DBPROPSET rgdbpsetInit[], - __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, - __out IDBCreateSession** ppidbSession -) -{ - Unused(szFriendlyClsidName); // only used in DEBUG builds - - HRESULT hr = S_OK; - IDBInitialize* pidbInitialize = NULL; - IDBProperties* pidbProperties = NULL; - - hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize); - SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName); - - // create and set the property set - hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); - SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName); - - hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit); - SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName); - - // initialize connection to datasource - hr = pidbInitialize->Initialize(); - if (FAILED(hr)) - { - DumpErrorRecords(); - } - SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName); - - hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); - SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName); - -LExit: - ReleaseObject(pidbProperties); - ReleaseObject(pidbInitialize); - - return hr; -} - -HRESULT DumpErrorRecords() -{ - HRESULT hr = S_OK; - IErrorInfo* pIErrorInfo = NULL; - IErrorRecords* pIErrorRecords = NULL; - IErrorInfo* pIErrorInfoRecord = NULL; - BSTR bstrDescription = NULL; - ULONG i = 0; - ULONG cRecords = 0; - ERRORINFO ErrorInfo = { }; - - // Get IErrorInfo pointer from OLE. - hr = ::GetErrorInfo(0, &pIErrorInfo); - if (FAILED(hr)) - { - ExitFunction(); - } - - // QI for IID_IErrorRecords. - hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); - if (FAILED(hr)) - { - ExitFunction(); - } - - // Get error record count. - hr = pIErrorRecords->GetRecordCount(&cRecords); - if (FAILED(hr)) - { - ExitFunction(); - } - - // Loop through the error records. - for (i = 0; i < cRecords; i++) - { - // Get pIErrorInfo from pIErrorRecords. - hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord); - - if (SUCCEEDED(hr)) - { - // Get error description and source. - hr = pIErrorInfoRecord->GetDescription(&bstrDescription); - - // Retrieve the ErrorInfo structures. - hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo); - - SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription); - - ReleaseNullObject(pIErrorInfoRecord); - ReleaseNullBSTR(bstrDescription); - } - } - -LExit: - ReleaseNullBSTR(bstrDescription); - ReleaseObject(pIErrorInfoRecord); - ReleaseObject(pIErrorRecords); - ReleaseObject(pIErrorInfo); - - return hr; -} - -/******************************************************************** - FileSpecToString - -*********************************************************************/ -static HRESULT FileSpecToString( - __in const SQL_FILESPEC* psf, - __out LPWSTR* ppwz - ) -{ - Assert(psf && ppwz); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - - hr = StrAllocString(&pwz, L"(", 1024); - SqlExitOnFailure(hr, "failed to allocate string for database file info"); - - SqlExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info"); - SqlExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info"); - - hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName); - SqlExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName); - - hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename); - SqlExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename); - - if (0 != psf->wzSize[0]) - { - hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize); - SqlExitOnFailure(hr, "failed to format database file info size: %ls", psf->wzSize); - } - - if (0 != psf->wzMaxSize[0]) - { - hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize); - SqlExitOnFailure(hr, "failed to format database file info maxsize: %ls", psf->wzMaxSize); - } - - if (0 != psf->wzGrow[0]) - { - hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow); - SqlExitOnFailure(hr, "failed to format database file info growth: %ls", psf->wzGrow); - } - - hr = StrAllocFormatted(&pwz, L"%s)", pwz); - SqlExitOnFailure(hr, "failed to allocate string for file spec"); - - *ppwz = pwz; - pwz = NULL; // null here so it doesn't get freed below - -LExit: - ReleaseStr(pwz); - return hr; -} - -static HRESULT EscapeSqlIdentifier( - __in_z LPCWSTR wzIdentifier, - __deref_out_z LPWSTR* ppwz - ) -{ - Assert(ppwz); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - - if (wzIdentifier == NULL) - { - //Just ignore a NULL identifier and clear out the result - ReleaseNullStr(*ppwz); - ExitFunction(); - } - - int cchIdentifier = lstrlenW(wzIdentifier); - - //If an empty string or already escaped just copy - if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']')) - { - hr = StrAllocString(&pwz, wzIdentifier, 0); - SqlExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier); - } - else - { - //escape it - hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier); - SqlExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier); - } - - *ppwz = pwz; - pwz = NULL; // null here so it doesn't get freed below - -LExit: - ReleaseStr(pwz); - return hr; -} diff --git a/src/dutil/srputil.cpp b/src/dutil/srputil.cpp deleted file mode 100644 index e44536cc..00000000 --- a/src/dutil/srputil.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define SrpExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) -#define SrpExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) -#define SrpExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) -#define SrpExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) -#define SrpExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SRPUTIL, e, x, s, __VA_ARGS__) -#define SrpExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SRPUTIL, g, x, s, __VA_ARGS__) - - -typedef BOOL (WINAPI *PFN_SETRESTOREPTW)( - __in PRESTOREPOINTINFOW pRestorePtSpec, - __out PSTATEMGRSTATUS pSMgrStatus - ); - -static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL; -static HMODULE vhSrClientDll = NULL; - - -static HRESULT InitializeComSecurity(); - - -DAPI_(HRESULT) SrpInitialize( - __in BOOL fInitializeComSecurity - ) -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"srclient.dll", &vhSrClientDll); - if (FAILED(hr)) - { - ExitFunction1(hr = E_NOTIMPL); - } - - vpfnSRSetRestorePointW = reinterpret_cast(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW")); - SrpExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address."); - - // If allowed, initialize COM security to enable NetworkService, - // LocalService and System to make callbacks to the process - // calling System Restore. This is required for any process - // that calls SRSetRestorePoint. - if (fInitializeComSecurity) - { - hr = InitializeComSecurity(); - SrpExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore."); - } - -LExit: - if (FAILED(hr) && vhSrClientDll) - { - SrpUninitialize(); - } - - return hr; -} - -DAPI_(void) SrpUninitialize() -{ - if (vhSrClientDll) - { - ::FreeLibrary(vhSrClientDll); - vhSrClientDll = NULL; - vpfnSRSetRestorePointW = NULL; - } -} - -DAPI_(HRESULT) SrpCreateRestorePoint( - __in_z LPCWSTR wzApplicationName, - __in SRP_ACTION action - ) -{ - HRESULT hr = S_OK; - RESTOREPOINTINFOW restorePoint = { }; - STATEMGRSTATUS status = { }; - - if (!vpfnSRSetRestorePointW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - restorePoint.dwEventType = BEGIN_SYSTEM_CHANGE; - restorePoint.dwRestorePtType = (SRP_ACTION_INSTALL == action) ? APPLICATION_INSTALL : (SRP_ACTION_UNINSTALL == action) ? APPLICATION_UNINSTALL : MODIFY_SETTINGS; - ::StringCbCopyW(restorePoint.szDescription, sizeof(restorePoint.szDescription), wzApplicationName); - - if (!vpfnSRSetRestorePointW(&restorePoint, &status)) - { - SrpExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point."); - } - -LExit: - return hr; -} - - -// internal functions. - -static HRESULT InitializeComSecurity() -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - SECURITY_DESCRIPTOR sd = {0}; - EXPLICIT_ACCESS ea[5] = {0}; - ACL* pAcl = NULL; - ULONGLONG rgSidBA[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidLS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidNS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidPS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidSY[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - DWORD cbSid = 0; - - // Create the security descriptor explicitly as follows because - // CoInitializeSecurity() will not accept the relative security descriptors - // returned by ConvertStringSecurityDescriptorToSecurityDescriptor(). - // - // The result is a security descriptor that is equivalent to the following - // security descriptor definition language (SDDL) string: - // - // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)(A;;0x1;;;SY)(A;;0x1;;;BA) - // - - // Initialize the security descriptor. - if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) - { - SrpExitWithLastError(hr, "Failed to initialize security descriptor for system restore."); - } - - // Create an administrator group security identifier (SID). - cbSid = sizeof(rgSidBA); - if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create administrator SID for system restore."); - } - - // Create a local service security identifier (SID). - cbSid = sizeof(rgSidLS); - if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create local service SID for system restore."); - } - - // Create a network service security identifier (SID). - cbSid = sizeof(rgSidNS); - if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create network service SID for system restore."); - } - - // Create a personal account security identifier (SID). - cbSid = sizeof(rgSidPS); - if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create self SID for system restore."); - } - - // Create a local service security identifier (SID). - cbSid = sizeof(rgSidSY); - if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create local system SID for system restore."); - } - - // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and - // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required. - ea[0].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[0].grfAccessMode = SET_ACCESS; - ea[0].grfInheritance = NO_INHERITANCE; - ea[0].Trustee.pMultipleTrustee = NULL; - ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[0].Trustee.ptstrName = (LPTSTR)rgSidBA; - - ea[1].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[1].grfAccessMode = SET_ACCESS; - ea[1].grfInheritance = NO_INHERITANCE; - ea[1].Trustee.pMultipleTrustee = NULL; - ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[1].Trustee.ptstrName = (LPTSTR)rgSidLS; - - ea[2].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[2].grfAccessMode = SET_ACCESS; - ea[2].grfInheritance = NO_INHERITANCE; - ea[2].Trustee.pMultipleTrustee = NULL; - ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[2].Trustee.ptstrName = (LPTSTR)rgSidNS; - - ea[3].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[3].grfAccessMode = SET_ACCESS; - ea[3].grfInheritance = NO_INHERITANCE; - ea[3].Trustee.pMultipleTrustee = NULL; - ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[3].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[3].Trustee.ptstrName = (LPTSTR)rgSidPS; - - ea[4].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[4].grfAccessMode = SET_ACCESS; - ea[4].grfInheritance = NO_INHERITANCE; - ea[4].Trustee.pMultipleTrustee = NULL; - ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[4].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[4].Trustee.ptstrName = (LPTSTR)rgSidSY; - - // Create an access control list (ACL) using this ACE list. - er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl); - SrpExitOnWin32Error(er, hr, "Failed to create ACL for system restore."); - - // Set the security descriptor owner to Administrators. - if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE)) - { - SrpExitWithLastError(hr, "Failed to set administrators owner for system restore."); - } - - // Set the security descriptor group to Administrators. - if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE)) - { - SrpExitWithLastError(hr, "Failed to set administrators group access for system restore."); - } - - // Set the discretionary access control list (DACL) to the ACL. - if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) - { - SrpExitWithLastError(hr, "Failed to set DACL for system restore."); - } - - // Note that an explicit security descriptor is being passed in. - 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); - SrpExitOnFailure(hr, "Failed to initialize COM security for system restore."); - -LExit: - if (pAcl) - { - ::LocalFree(pAcl); - } - - return hr; -} diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp deleted file mode 100644 index 550d6169..00000000 --- a/src/dutil/strutil.cpp +++ /dev/null @@ -1,2824 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define StrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) -#define StrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) -#define StrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) -#define StrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) -#define StrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_STRUTIL, e, x, s, __VA_ARGS__) -#define StrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_STRUTIL, g, x, s, __VA_ARGS__) - -#define ARRAY_GROWTH_SIZE 5 - -// Forward declarations. -static HRESULT AllocHelper( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch, - __in BOOL fZeroOnRealloc - ); -static HRESULT AllocStringHelper( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ); -static HRESULT AllocConcatHelper( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ); -static HRESULT AllocFormattedArgsHelper( - __deref_out_z LPWSTR* ppwz, - __in BOOL fZeroOnRealloc, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ); -static HRESULT StrAllocStringMapInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in DWORD dwMapFlags - ); - -/******************************************************************** -StrAlloc - allocates or reuses dynamic string memory - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAlloc( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ) -{ - return AllocHelper(ppwz, cch, FALSE); -} - -/******************************************************************** -StrAllocSecure - allocates or reuses dynamic string memory -If the memory needs to reallocated, calls SecureZeroMemory on the -original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAllocSecure( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ) -{ - return AllocHelper(ppwz, cch, TRUE); -} - -/******************************************************************** -AllocHelper - allocates or reuses dynamic string memory -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -static HRESULT AllocHelper( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch, - __in BOOL fZeroOnRealloc - ) -{ - Assert(ppwz && cch); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppwz) - { - if (fZeroOnRealloc) - { - LPVOID pvNew = NULL; - hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew); - StrExitOnFailure(hr, "Failed to reallocate string"); - pwz = static_cast(pvNew); - } - else - { - pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE)); - } - } - else - { - pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); - } - - StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppwz = pwz; -LExit: - return hr; -} - - -/******************************************************************** -StrTrimCapacity - Frees any unnecessary memory associated with a string. - Purely used for optimization, generally only when a string - has been changing size, and will no longer grow. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -HRESULT DAPI StrTrimCapacity( - __deref_out_z LPWSTR* ppwz - ) -{ - Assert(ppwz); - - HRESULT hr = S_OK; - SIZE_T cchLen = 0; - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - StrExitOnRootFailure(hr, "Failed to calculate length of string"); - - ++cchLen; // Add 1 for null-terminator - - hr = StrAlloc(ppwz, cchLen); - StrExitOnFailure(hr, "Failed to reallocate string"); - -LExit: - return hr; -} - - -/******************************************************************** -StrTrimWhitespace - allocates or reuses dynamic string memory and copies - in an existing string, excluding whitespace - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -HRESULT DAPI StrTrimWhitespace( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource - ) -{ - HRESULT hr = S_OK; - size_t i = 0; - LPWSTR sczResult = NULL; - - // Ignore beginning whitespace - while (L' ' == *wzSource || L'\t' == *wzSource) - { - wzSource++; - } - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &i); - StrExitOnRootFailure(hr, "Failed to get length of string"); - - // Overwrite ending whitespace with null characters - if (0 < i) - { - // start from the last non-null-terminator character in the array - for (i = i - 1; i > 0; --i) - { - if (L' ' != wzSource[i] && L'\t' != wzSource[i]) - { - break; - } - } - - ++i; - } - - hr = StrAllocString(&sczResult, wzSource, i); - StrExitOnFailure(hr, "Failed to copy result string"); - - // Output result - *ppwz = sczResult; - sczResult = NULL; - -LExit: - ReleaseStr(sczResult); - - return hr; -} - - -/******************************************************************** -StrAnsiAlloc - allocates or reuses dynamic ANSI string memory - -NOTE: caller is responsible for freeing ppsz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAlloc( - __deref_out_ecount_part(cch, 0) LPSTR* ppsz, - __in SIZE_T cch - ) -{ - Assert(ppsz && cch); - - HRESULT hr = S_OK; - LPSTR psz = NULL; - - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppsz) - { - psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE)); - } - else - { - psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); - } - - StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppsz = psz; -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string. - Purely used for optimization, generally only when a string - has been changing size, and will no longer grow. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -HRESULT DAPI StrAnsiTrimCapacity( - __deref_out_z LPSTR* ppz - ) -{ - Assert(ppz); - - HRESULT hr = S_OK; - SIZE_T cchLen = 0; - -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); -#pragma prefast(pop) - StrExitOnFailure(hr, "Failed to calculate length of string"); - - ++cchLen; // Add 1 for null-terminator - - hr = StrAnsiAlloc(ppz, cchLen); - StrExitOnFailure(hr, "Failed to reallocate string"); - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies - in an existing string, excluding whitespace - -NOTE: caller is responsible for freeing ppz even if function fails -********************************************************************/ -HRESULT DAPI StrAnsiTrimWhitespace( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR szSource - ) -{ - HRESULT hr = S_OK; - size_t i = 0; - LPSTR sczResult = NULL; - - // Ignore beginning whitespace - while (' ' == *szSource || '\t' == *szSource) - { - szSource++; - } - - hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, &i); - StrExitOnRootFailure(hr, "Failed to get length of string"); - - // Overwrite ending whitespace with null characters - if (0 < i) - { - // start from the last non-null-terminator character in the array - for (i = i - 1; i > 0; --i) - { - if (L' ' != szSource[i] && L'\t' != szSource[i]) - { - break; - } - } - - ++i; - } - - hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i); - StrExitOnFailure(hr, "Failed to copy result string"); - - // Output result - *ppz = sczResult; - sczResult = NULL; - -LExit: - ReleaseStr(sczResult); - - return hr; -} - -/******************************************************************** -StrAllocString - allocates or reuses dynamic string memory and copies in an existing string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocString( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocStringHelper(ppwz, wzSource, cchSource, FALSE); -} - -/******************************************************************** -StrAllocStringSecure - allocates or reuses dynamic string memory and -copies in an existing string. If the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocStringSecure( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocStringHelper(ppwz, wzSource, cchSource, TRUE); -} - -/******************************************************************** -AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -static HRESULT AllocStringHelper( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ) -{ - Assert(ppwz && wzSource); // && *wzSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource && wzSource) - { - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - StrExitOnRootFailure(hr, "failed to get length of source string"); - } - - SIZE_T cchNeeded; - hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - StrExitOnRootFailure(hr, "source string is too long"); - - if (cch < cchNeeded) - { - cch = cchNeeded; - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string from string."); - } - - // copy everything (the NULL terminator will be included) - hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string - -NOTE: caller is responsible for freeing ppsz even if function fails -NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocString( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ) -{ - Assert(ppsz && wzSource); - - HRESULT hr = S_OK; - LPSTR psz = NULL; - SIZE_T cch = 0; - SIZE_T cchDest = cchSource; // at least enough - - if (*ppsz) - { - cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource) - { - cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL); - if (0 == cchDest) - { - StrExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource); - } - - --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below - } - 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 - { - cchDest = cchSource - 1; - } - - if (cch < cchDest + 1) - { - cch = cchDest + 1; // add one for the NULL terminator - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppsz) - { - psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE)); - } - else - { - psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); - } - StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppsz = psz; - } - - if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL)) - { - StrExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource); - } - (*ppsz)[cchDest] = L'\0'; - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ) -{ - Assert(ppwz && szSource); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - SIZE_T cch = 0; - SIZE_T cchDest = cchSource; // at least enough - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource) - { - cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0); - if (0 == cchDest) - { - StrExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource); - } - - --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below - } - 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 - { - cchDest = cchSource - 1; - } - - if (cch < cchDest + 1) - { - cch = cchDest + 1; - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppwz) - { - pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE)); - } - else - { - pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); - } - - StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppwz = pwz; - } - - if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch)) - { - StrExitWithLastError(hr, "failed to convert to unicode: %s", szSource); - } - (*ppwz)[cchDest] = L'\0'; - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string - -NOTE: caller is responsible for freeing ppsz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -HRESULT DAPI StrAnsiAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource - ) -{ - Assert(ppsz && szSource); // && *szSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - - if (*ppsz) - { - cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnRootFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource && szSource) - { - hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - StrExitOnRootFailure(hr, "failed to get length of source string"); - } - - SIZE_T cchNeeded; - hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - StrExitOnRootFailure(hr, "source string is too long"); - - if (cch < cchNeeded) - { - cch = cchNeeded; - hr = StrAnsiAlloc(ppsz, cch); - StrExitOnFailure(hr, "failed to allocate string from string."); - } - - // copy everything (the NULL terminator will be included) -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); -#pragma prefast(pop) - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocPrefix - allocates or reuses dynamic string memory and - prefixes a string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchPrefix does not have to equal the length of wzPrefix -NOTE: if cchPrefix == 0, length of wzPrefix is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocPrefix( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzPrefix, - __in SIZE_T cchPrefix - ) -{ - Assert(ppwz && wzPrefix); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cchLen = 0; - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - Assert(cchLen <= cch); - - if (0 == cchPrefix) - { - hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast(&cchPrefix)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - if (cch - cchLen < cchPrefix + 1) - { - cch = cchPrefix + cchLen + 1; - hr = StrAlloc(ppwz, cch); - StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix); - } - - if (*ppwz) - { - SIZE_T cb = cch * sizeof(WCHAR); - SIZE_T cbPrefix = cchPrefix * sizeof(WCHAR); - - memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix); - memcpy(*ppwz, wzPrefix, cbPrefix); - } - else - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "for some reason our buffer is still null"); - } - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocConcat( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE); -} - - -/******************************************************************** -StrAllocConcatSecure - allocates or reuses dynamic string memory and -adds an existing string. If the memory needs to reallocated, calls -SecureZeroMemory on the original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocConcatSecure( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE); -} - - -/******************************************************************** -AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -static HRESULT AllocConcatHelper( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ) -{ - Assert(ppwz && wzSource); // && *wzSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cchLen = 0; - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - Assert(cchLen <= cch); - - if (0 == cchSource) - { - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - if (cch - cchLen < cchSource + 1) - { - cch = (cchSource + cchLen + 1) * 2; - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource); - } - - if (*ppwz) - { - hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - } - else - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "for some reason our buffer is still null"); - } - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string - -NOTE: caller is responsible for freeing ppz even if function fails -NOTE: cchSource does not have to equal the length of pzSource -NOTE: if cchSource == 0, length of pzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocConcat( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR pzSource, - __in SIZE_T cchSource - ) -{ - Assert(ppz && pzSource); // && *pzSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cchLen = 0; - - if (*ppz) - { - cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); // convert the count in bytes to count in characters - -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); -#pragma prefast(pop) - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - Assert(cchLen <= cch); - - if (0 == cchSource) - { -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); -#pragma prefast(pop) - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - if (cch - cchLen < cchSource + 1) - { - cch = (cchSource + cchLen + 1) * 2; - hr = StrAnsiAlloc(ppz, cch); - StrExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource); - } - - if (*ppz) - { -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); -#pragma prefast(pop) - } - else - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "for some reason our buffer is still null"); - } - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocFormatted - allocates or reuses dynamic string memory and formats it - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgs(ppwz, wzFormat, args); - va_end(args); - - return hr; -} - - -/******************************************************************** -StrAllocConcatFormatted - allocates or reuses dynamic string memory -and adds a formatted string - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocConcatFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - LPWSTR sczFormatted = NULL; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args); - va_end(args); - StrExitOnFailure(hr, "Failed to allocate formatted string"); - - hr = StrAllocConcat(ppwz, sczFormatted, 0); - -LExit: - ReleaseStr(sczFormatted); - - return hr; -} - - -/******************************************************************** -StrAllocConcatFormattedSecure - allocates or reuses dynamic string -memory and adds a formatted string. If the memory needs to be -reallocated, calls SecureZeroMemory on original block of memory after -it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - LPWSTR sczFormatted = NULL; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args); - va_end(args); - StrExitOnFailure(hr, "Failed to allocate formatted string"); - - hr = StrAllocConcatSecure(ppwz, sczFormatted, 0); - -LExit: - ReleaseStr(sczFormatted); - - return hr; -} - - -/******************************************************************** -StrAllocFormattedSecure - allocates or reuses dynamic string memory -and formats it. If the memory needs to be reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); - va_end(args); - - return hr; -} - - -/******************************************************************** -StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it - -NOTE: caller is responsible for freeing ppsz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocFormatted( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - ... - ) -{ - Assert(ppsz && szFormat && *szFormat); - - HRESULT hr = S_OK; - va_list args; - - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args); - va_end(args); - - return hr; -} - - -/******************************************************************** -StrAllocFormattedArgs - allocates or reuses dynamic string memory -and formats it with the passed in args - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAllocFormattedArgs( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ) -{ - return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args); -} - - -/******************************************************************** -StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory -and formats it with the passed in args. - -If the memory needs to reallocated, calls SecureZeroMemory on the -original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAllocFormattedArgsSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ) -{ - return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args); -} - - -/******************************************************************** -AllocFormattedArgsHelper - allocates or reuses dynamic string memory -and formats it with the passed in args. - -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -static HRESULT AllocFormattedArgsHelper( - __deref_out_z LPWSTR* ppwz, - __in BOOL fZeroOnRealloc, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - LPWSTR pwzOriginal = NULL; - SIZE_T cbOriginal = 0; - size_t cchOriginal = 0; - - if (*ppwz) - { - cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cbOriginal) - { - hr = E_INVALIDARG; - StrExitOnRootFailure(hr, "failed to get size of destination string"); - } - - cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, &cchOriginal); - StrExitOnRootFailure(hr, "failed to get length of original string"); - } - - if (0 == cch) // if there is no space in the string buffer - { - cch = 256; - - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); - } - - // format the message (grow until it fits or there is a failure) - do - { - hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - if (!pwzOriginal) - { - // this allows you to pass the original string as a formatting argument and not crash - // save the original string and free it after the printf is complete - pwzOriginal = *ppwz; - *ppwz = NULL; - - // StringCchVPrintfW starts writing to the string... - // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); - pwzOriginal[cchOriginal] = 0; - } - - cch *= 2; - - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); - - hr = S_FALSE; - } - } while (S_FALSE == hr); - StrExitOnRootFailure(hr, "failed to format string"); - -LExit: - if (pwzOriginal && fZeroOnRealloc) - { - SecureZeroMemory(pwzOriginal, cbOriginal); - } - - ReleaseStr(pwzOriginal); - - return hr; -} - - -/******************************************************************** -StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory -and formats it with the passed in args - -NOTE: caller is responsible for freeing ppsz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - __in va_list args - ) -{ - Assert(ppsz && szFormat && *szFormat); - - HRESULT hr = S_OK; - SIZE_T cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; - LPSTR pszOriginal = NULL; - size_t cchOriginal = 0; - - if (*ppsz) - { - cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnRootFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthA(*ppsz, STRSAFE_MAX_CCH, &cchOriginal); - StrExitOnRootFailure(hr, "failed to get length of original string"); - } - - if (0 == cch) // if there is no space in the string buffer - { - cch = 256; - hr = StrAnsiAlloc(ppsz, cch); - StrExitOnFailure(hr, "failed to allocate string to format: %s", szFormat); - } - - // format the message (grow until it fits or there is a failure) - do - { -#pragma prefast(push) -#pragma prefast(disable:25068) // We intentionally don't use the unicode API here - hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args); -#pragma prefast(pop) - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - if (!pszOriginal) - { - // this allows you to pass the original string as a formatting argument and not crash - // save the original string and free it after the printf is complete - pszOriginal = *ppsz; - *ppsz = NULL; - // StringCchVPrintfW starts writing to the string... - // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); - pszOriginal[cchOriginal] = 0; - } - cch *= 2; - hr = StrAnsiAlloc(ppsz, cch); - StrExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat); - hr = S_FALSE; - } - } while (S_FALSE == hr); - StrExitOnRootFailure(hr, "failed to format string"); - -LExit: - ReleaseStr(pszOriginal); - - return hr; -} - - -/******************************************************************** -StrAllocFromError - returns the string for a particular error. - -********************************************************************/ -extern "C" HRESULT DAPI StrAllocFromError( - __inout LPWSTR *ppwzMessage, - __in HRESULT hrError, - __in_opt HMODULE hModule, - ... - ) -{ - HRESULT hr = S_OK; - DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM; - LPVOID pvMessage = NULL; - DWORD cchMessage = 0; - - if (hModule) - { - dwFlags |= FORMAT_MESSAGE_FROM_HMODULE; - } - - va_list args; - va_start(args, hModule); - cchMessage = ::FormatMessageW(dwFlags, static_cast(hModule), hrError, 0, reinterpret_cast(&pvMessage), 0, &args); - va_end(args); - - if (0 == cchMessage) - { - StrExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError); - } - - hr = StrAllocString(ppwzMessage, reinterpret_cast(pvMessage), cchMessage); - StrExitOnFailure(hr, "Failed to allocate string for message."); - -LExit: - if (pvMessage) - { - ::LocalFree(pvMessage); - } - - return hr; -} - - -/******************************************************************** -StrMaxLength - returns maximum number of characters that can be stored in dynamic string p - -NOTE: assumes Unicode string -********************************************************************/ -extern "C" HRESULT DAPI StrMaxLength( - __in LPCVOID p, - __out SIZE_T* pcch - ) -{ - Assert(pcch); - - HRESULT hr = S_OK; - - if (p) - { - *pcch = MemSize(p); // get size of entire buffer - if (-1 == *pcch) - { - ExitFunction1(hr = E_FAIL); - } - - *pcch /= sizeof(WCHAR); // reduce to count of characters - } - else - { - *pcch = 0; - } - Assert(S_OK == hr); - -LExit: - return hr; -} - - -/******************************************************************** -StrSize - returns count of bytes in dynamic string p - -********************************************************************/ -extern "C" HRESULT DAPI StrSize( - __in LPCVOID p, - __out SIZE_T* pcb - ) -{ - Assert(p && pcb); - - HRESULT hr = S_OK; - - *pcb = MemSize(p); - if (-1 == *pcb) - { - hr = E_FAIL; - } - - return hr; -} - -/******************************************************************** -StrFree - releases dynamic string memory allocated by any StrAlloc*() functions - -********************************************************************/ -extern "C" HRESULT DAPI StrFree( - __in LPVOID p - ) -{ - Assert(p); - - HRESULT hr = MemFree(p); - StrExitOnFailure(hr, "failed to free string"); - -LExit: - return hr; -} - - -/**************************************************************************** -StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString. -Replaces all instances. - -****************************************************************************/ -extern "C" HRESULT DAPI StrReplaceStringAll( - __inout LPWSTR* ppwzOriginal, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ) -{ - HRESULT hr = S_OK; - DWORD dwStartIndex = 0; - - do - { - hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString); - StrExitOnFailure(hr, "Failed to replace substring"); - } - while (S_OK == hr); - - hr = (0 == dwStartIndex) ? S_FALSE : S_OK; - -LExit: - return hr; -} - - -/**************************************************************************** -StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString. -Search for old substring starts at dwStartIndex. Does only 1 replace. - -****************************************************************************/ -extern "C" HRESULT DAPI StrReplaceString( - __inout LPWSTR* ppwzOriginal, - __inout DWORD* pdwStartIndex, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ) -{ - Assert(ppwzOriginal && wzOldSubString && wzNewSubString); - - HRESULT hr = S_FALSE; - LPCWSTR wzSubLocation = NULL; - LPWSTR pwzBuffer = NULL; - size_t cchOldSubString = 0; - size_t cchNewSubString = 0; - - if (!*ppwzOriginal) - { - ExitFunction(); - } - - wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString); - if (!wzSubLocation) - { - ExitFunction(); - } - - if (wzOldSubString) - { - hr = ::StringCchLengthW(wzOldSubString, STRSAFE_MAX_CCH, &cchOldSubString); - StrExitOnRootFailure(hr, "Failed to get old string length."); - } - - if (wzNewSubString) - { - hr = ::StringCchLengthW(wzNewSubString, STRSAFE_MAX_CCH, &cchNewSubString); - StrExitOnRootFailure(hr, "Failed to get new string length."); - } - - hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex); - StrExitOnRootFailure(hr, "Failed to diff pointers."); - - hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal); - StrExitOnFailure(hr, "Failed to duplicate string."); - - pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0'; - - hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0); - StrExitOnFailure(hr, "Failed to append new string."); - - hr = StrAllocConcat(&pwzBuffer, wzSubLocation + cchOldSubString, 0); - StrExitOnFailure(hr, "Failed to append post string."); - - hr = StrFree(*ppwzOriginal); - StrExitOnFailure(hr, "Failed to free original string."); - - *ppwzOriginal = pwzBuffer; - *pdwStartIndex = *pdwStartIndex + static_cast(cchNewSubString); - hr = S_OK; - -LExit: - return hr; -} - - -static inline BYTE HexCharToByte( - __in WCHAR wc - ) -{ - 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 - - BYTE b; - if (L'0' <= wc && wc <= L'9') - { - b = (BYTE)(wc - L'0'); - } - else if ('a' <= wc && wc <= 'f') - { - b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1)); - } - else // must be (L'A' <= wc && wc <= L'F') - { - b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1)); - } - - Assert(0 <= b && b <= 15); - return b; -} - - -/**************************************************************************** -StrHexEncode - converts an array of bytes to a text string - -NOTE: wzDest must have space for cbSource * 2 + 1 characters -****************************************************************************/ -extern "C" HRESULT DAPI StrHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out_ecount(cchDest) LPWSTR wzDest, - __in SIZE_T cchDest - ) -{ - Assert(pbSource && wzDest); - - HRESULT hr = S_OK; - DWORD i; - BYTE b; - - if (cchDest < 2 * cbSource + 1) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); - } - - for (i = 0; i < cbSource; ++i) - { - b = (*pbSource) >> 4; - *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); - b = (*pbSource) & 0xF; - *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); - - ++pbSource; - } - - *wzDest = 0; - -LExit: - return hr; -} - - -/**************************************************************************** -StrAllocHexEncode - converts an array of bytes to an allocated text string - -****************************************************************************/ -HRESULT DAPI StrAllocHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest - ) -{ - HRESULT hr = S_OK; - SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1); - - hr = StrAlloc(ppwzDest, cchSource); - StrExitOnFailure(hr, "Failed to allocate hex string."); - - hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource); - StrExitOnFailure(hr, "Failed to encode hex string."); - -LExit: - return hr; -} - - -/**************************************************************************** -StrHexDecode - converts a string of text to array of bytes - -NOTE: wzSource must contain even number of characters -****************************************************************************/ -extern "C" HRESULT DAPI StrHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(cbDest) BYTE* pbDest, - __in SIZE_T cbDest - ) -{ - Assert(wzSource && pbDest); - - HRESULT hr = S_OK; - size_t cchSource = 0; - size_t i = 0; - BYTE b = 0; - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); - StrExitOnRootFailure(hr, "Failed to get length of hex string: %ls", wzSource); - - Assert(0 == cchSource % 2); - if (cbDest < cchSource / 2) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %Iu into %Iu bytes.", wzSource, cchSource, cbDest); - } - - for (i = 0; i < cchSource / 2; ++i) - { - b = HexCharToByte(*wzSource++); - (*pbDest) = b << 4; - - b = HexCharToByte(*wzSource++); - (*pbDest) |= b & 0xF; - - ++pbDest; - } - -LExit: - return hr; -} - - -/**************************************************************************** -StrAllocHexDecode - allocates a byte array hex-converted from string of text - -NOTE: wzSource must contain even number of characters -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(*pcbDest) BYTE** ppbDest, - __out_opt DWORD* pcbDest - ) -{ - Assert(wzSource && *wzSource && ppbDest); - - HRESULT hr = S_OK; - size_t cch = 0; - BYTE* pb = NULL; - DWORD cb = 0; - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch); - StrExitOnFailure(hr, "Failed to calculate length of source string."); - - if (cch % 2) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded."); - } - - cb = static_cast(cch / 2); - pb = static_cast(MemAlloc(cb, TRUE)); - StrExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode."); - - hr = StrHexDecode(wzSource, pb, cb); - StrExitOnFailure(hr, "Failed to decode source string."); - - *ppbDest = pb; - pb = NULL; - - if (pcbDest) - { - *pcbDest = cb; - } - -LExit: - ReleaseMem(pb); - - return hr; -} - - -/**************************************************************************** -Base85 encoding/decoding data - -****************************************************************************/ -const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"; - -const BYTE Base85DecodeTable[256] = -{ - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54, - 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85 -}; - -const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 }; - - -/**************************************************************************** -StrAllocBase85Encode - converts an array of bytes into an XML compatible string - -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocBase85Encode( - __in_bcount_opt(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_z LPWSTR* pwzDest - ) -{ - HRESULT hr = S_OK; - SIZE_T cchDest = 0; - LPWSTR wzDest; - DWORD_PTR iSource = 0; - DWORD_PTR iDest = 0; - - if (!pwzDest || !pbSource) - { - return E_INVALIDARG; - } - - // calc actual size of output - cchDest = cbSource / 4; - cchDest += cchDest * 4; - if (cbSource & 3) - { - cchDest += (cbSource & 3) + 1; - } - ++cchDest; // add room for null terminator - - hr = StrAlloc(pwzDest, cchDest); - StrExitOnFailure(hr, "failed to allocate destination string"); - - wzDest = *pwzDest; - - // first, encode full words - for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5) - { - DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24); - DWORD k = n / 85; - - //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); - wzDest[iDest] = Base85EncodeTable[n - k * 85]; - n = k / 85; - - //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); - wzDest[iDest + 1] = Base85EncodeTable[k - n * 85]; - k = n / 85; - - //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); - wzDest[iDest + 2] = Base85EncodeTable[n - k * 85]; - n = k / 85; - - //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); - wzDest[iDest + 3] = Base85EncodeTable[k - n * 85]; - - __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85); - - //Assert(0 <= n && n < countof(Base85EncodeTable)); - wzDest[iDest + 4] = Base85EncodeTable[n]; - } - - // encode any remaining bytes - if (iSource < cbSource) - { - DWORD n = 0; - for (DWORD i = 0; iSource + i < cbSource; ++i) - { - n += pbSource[iSource + i] << (i << 3); - } - - for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest) - { - DWORD k = n / 85; - - //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); - wzDest[iDest] = Base85EncodeTable[n - k * 85]; - - n = k; - } - - wzDest[iDest] = Base85EncodeTable[n]; - ++iDest; - } - Assert(iSource == cbSource); - Assert(iDest == cchDest - 1); - - wzDest[iDest] = L'\0'; - hr = S_OK; - -LExit: - return hr; -} - - -/**************************************************************************** -StrAllocBase85Decode - converts a string of text to array of bytes - -NOTE: Use MemFree() to release the allocated stream of bytes -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocBase85Decode( - __in_z LPCWSTR wzSource, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out SIZE_T* pcbDest - ) -{ - HRESULT hr = S_OK; - size_t cchSource = 0; - DWORD_PTR i, n, k; - - BYTE* pbDest = 0; - SIZE_T cbDest = 0; - - if (!wzSource || !ppbDest || !pcbDest) - { - ExitFunction1(hr = E_INVALIDARG); - } - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); - StrExitOnRootFailure(hr, "failed to get length of base 85 string: %ls", wzSource); - - // evaluate size of output and check it - k = cchSource / 5; - cbDest = k << 2; - k = cchSource - k * 5; - if (k) - { - if (1 == k) - { - // decode error -- encoded size cannot equal 1 mod 5 - return E_UNEXPECTED; - } - - cbDest += k - 1; - } - - *ppbDest = static_cast(MemAlloc(cbDest, FALSE)); - StrExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string"); - - pbDest = *ppbDest; - *pcbDest = cbDest; - - // decode full words first - while (5 <= cchSource) - { - k = Base85DecodeTable[wzSource[0]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n = k; - - k = Base85DecodeTable[wzSource[1]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n += k * 85; - - k = Base85DecodeTable[wzSource[2]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n += k * (85 * 85); - - k = Base85DecodeTable[wzSource[3]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n += k * (85 * 85 * 85); - - k = Base85DecodeTable[wzSource[4]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - k *= (85 * 85 * 85 * 85); - - // if (k + n > (1u << 32)) <=> (k > ~n) then decode error - if (k > ~n) - { - // overflow - return E_UNEXPECTED; - } - - n += k; - - pbDest[0] = (BYTE) n; - pbDest[1] = (BYTE) (n >> 8); - pbDest[2] = (BYTE) (n >> 16); - pbDest[3] = (BYTE) (n >> 24); - - wzSource += 5; - pbDest += 4; - cchSource -= 5; - } - - if (cchSource) - { - n = 0; - for (i = 0; i < cchSource; ++i) - { - k = Base85DecodeTable[wzSource[i]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - - n += k * Base85PowerTable[i]; - } - - for (i = 1; i < cchSource; ++i) - { - *pbDest++ = (BYTE)n; - n >>= 8; - } - - if (0 != n) - { - // decode error - return E_UNEXPECTED; - } - } - - hr = S_OK; - -LExit: - return hr; -} - - -/**************************************************************************** -MultiSzLen - calculates the length of a MULTISZ string including all nulls -including the double null terminator at the end of the MULTISZ. - -NOTE: returns 0 if the multisz in not properly terminated with two nulls -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzLen( - __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, - __out SIZE_T* pcch -) -{ - Assert(pcch); - - HRESULT hr = S_OK; - LPCWSTR wz = pwzMultiSz; - DWORD_PTR dwMaxSize = 0; - - hr = StrMaxLength(pwzMultiSz, &dwMaxSize); - StrExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length"); - - *pcch = 0; - while (*pcch < dwMaxSize) - { - if (L'\0' == *wz && L'\0' == *(wz + 1)) - { - break; - } - - ++wz; - *pcch = *pcch + 1; - } - - // Add two for the last 2 NULLs (that we looked ahead at) - *pcch = *pcch + 2; - - // If we've walked off the end then the length is 0 - if (*pcch > dwMaxSize) - { - *pcch = 0; - } - -LExit: - return hr; -} - - -/**************************************************************************** -MultiSzPrepend - prepends a string onto the front of a MUTLISZ - -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzPrepend( - __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in __nullnullterminated LPCWSTR pwzInsert - ) -{ - Assert(ppwzMultiSz && pwzInsert && *pwzInsert); - - HRESULT hr =S_OK; - LPWSTR pwzResult = NULL; - SIZE_T cchResult = 0; - SIZE_T cchInsert = 0; - SIZE_T cchMultiSz = 0; - - // Get the lengths of the MULTISZ (and prime it if it's not initialized) - if (pcchMultiSz && 0 != *pcchMultiSz) - { - cchMultiSz = *pcchMultiSz; - } - else - { - hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get length of multisz"); - } - - hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchInsert)); - StrExitOnRootFailure(hr, "failed to get length of insert string"); - - cchResult = cchInsert + cchMultiSz + 1; - - // Allocate the result buffer - hr = StrAlloc(&pwzResult, cchResult + 1); - StrExitOnFailure(hr, "failed to allocate result string"); - - // Prepend - hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert); - StrExitOnRootFailure(hr, "failed to copy prepend string: %ls", pwzInsert); - - // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in - if (0 == cchMultiSz) - { - pwzResult[cchResult] = L'\0'; - ++cchResult; - } - else - { - // Copy the rest - ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR)); - - // Free the old buffer - ReleaseNullStr(*ppwzMultiSz); - } - - // Set the result - *ppwzMultiSz = pwzResult; - - if (pcchMultiSz) - { - *pcchMultiSz = cchResult; - } - - pwzResult = NULL; - -LExit: - ReleaseNullStr(pwzResult); - - return hr; -} - -/**************************************************************************** -MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the -specified sub string and returns the index of the -string in the MULTISZ, the address, neither, or both - -NOTE: returns S_FALSE if the string is not found -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzFindSubstring( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzSubstring, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn - ) -{ - Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring); - - HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty) - LPCWSTR wz = pwzMultiSz; - DWORD_PTR dwIndex = 0; - SIZE_T cchMultiSz = 0; - SIZE_T cchProgress = 0; - - hr = MultiSzLen(pwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - - // Find the string containing the sub string - hr = S_OK; - while (NULL == wcsistr(wz, pwzSubstring)) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMultiSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) - { - hr = S_FALSE; - break; - } - - // Move on to the next string - ++wz; - ++dwIndex; - } - Assert(S_OK == hr || S_FALSE == hr); - - // If we found it give them what they want - if (S_OK == hr) - { - if (pdwIndex) - { - *pdwIndex = dwIndex; - } - - if (ppwzFoundIn) - { - *ppwzFoundIn = wz; - } - } - -LExit: - return hr; -} - -/**************************************************************************** -MultiSzFindString - finds a string in a MULTISZ and returns the index of -the string in the MULTISZ, the address or both - -NOTE: returns S_FALSE if the string is not found -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzFindString( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzString, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound - ) -{ - Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound)); - - HRESULT hr = S_FALSE; // Assume we won't find it - LPCWSTR wz = pwzMultiSz; - DWORD_PTR dwIndex = 0; - SIZE_T cchMutliSz = 0; - SIZE_T cchProgress = 0; - - hr = MultiSzLen(pwzMultiSz, &cchMutliSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - - // Find the string - hr = S_OK; - while (0 != lstrcmpW(wz, pwzString)) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMutliSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz) - { - hr = S_FALSE; - break; - } - - // Move on to the next string - ++wz; - ++dwIndex; - } - Assert(S_OK == hr || S_FALSE == hr); - - // If we found it give them what they want - if (S_OK == hr) - { - if (pdwIndex) - { - *pdwIndex = dwIndex; - } - - if (ppwzFound) - { - *ppwzFound = wz; - } - } - -LExit: - return hr; -} - -/**************************************************************************** -MultiSzRemoveString - removes string from a MULTISZ at the specified -index - -NOTE: does an in place removal without shrinking the memory allocation - -NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzRemoveString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex - ) -{ - Assert(ppwzMultiSz && *ppwzMultiSz); - - HRESULT hr = S_OK; - LPCWSTR wz = *ppwzMultiSz; - LPCWSTR wzNext = NULL; - DWORD_PTR dwCurrentIndex = 0; - SIZE_T cchMultiSz = 0; - SIZE_T cchProgress = 0; - - hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - - // Find the index we want to remove - hr = S_OK; - while (dwCurrentIndex < dwIndex) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMultiSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) - { - hr = S_FALSE; - break; - } - - // Move on to the next string - ++wz; - ++cchProgress; - ++dwCurrentIndex; - } - Assert(S_OK == hr || S_FALSE == hr); - - // If we found the index to be removed - if (S_OK == hr) - { - wzNext = wz; - - // Slide through to the end of the current string - while (L'\0' != *wzNext && cchProgress < cchMultiSz) - { - ++wzNext; - ++cchProgress; - } - - // Something weird has happened if we're past the end of the MULTISZ - if (cchProgress > cchMultiSz) - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ"); - } - - // Move on to the next character - ++wzNext; - ++cchProgress; - - ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR)); - } - -LExit: - return hr; -} - -/**************************************************************************** -MultiSzInsertString - inserts new string at the specified index - -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzInsertString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzInsert - ) -{ - Assert(ppwzMultiSz && pwzInsert && *pwzInsert); - - HRESULT hr = S_OK; - LPCWSTR wz = *ppwzMultiSz; - DWORD_PTR dwCurrentIndex = 0; - SIZE_T cchProgress = 0; - LPWSTR pwzResult = NULL; - SIZE_T cchResult = 0; - SIZE_T cchString = 0; - SIZE_T cchMultiSz = 0; - - hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchString)); - StrExitOnRootFailure(hr, "failed to get length of insert string"); - - if (pcchMultiSz && 0 != *pcchMultiSz) - { - cchMultiSz = *pcchMultiSz; - } - else - { - hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - } - - // Find the index we want to insert at - hr = S_OK; - while (dwCurrentIndex < dwIndex) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMultiSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz) - { - hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND); - StrExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex); - } - - // Move on to the next string - ++wz; - ++cchProgress; - ++dwCurrentIndex; - } - - // - // Insert the string - // - cchResult = cchMultiSz + cchString + 1; - - hr = StrAlloc(&pwzResult, cchResult); - StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); - - // Copy the part before the insert - ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR)); - - // Copy the insert part - ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR)); - - // Copy the part after the insert - ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR)); - - // Free the old buffer - ReleaseNullStr(*ppwzMultiSz); - - // Set the result - *ppwzMultiSz = pwzResult; - - // If they wanted the resulting length, let 'em have it - if (pcchMultiSz) - { - *pcchMultiSz = cchResult; - } - - pwzResult = NULL; - -LExit: - ReleaseStr(pwzResult); - - return hr; -} - -/**************************************************************************** -MultiSzReplaceString - replaces string at the specified index with a new one - -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzReplaceString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzString - ) -{ - Assert(ppwzMultiSz && pwzString && *pwzString); - - HRESULT hr = S_OK; - - hr = MultiSzRemoveString(ppwzMultiSz, dwIndex); - StrExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex); - - hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString); - StrExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex); - -LExit: - return hr; -} - - -/**************************************************************************** -wcsistr - case insensitive find a substring - -****************************************************************************/ -extern "C" LPCWSTR DAPI wcsistr( - __in_z LPCWSTR wzString, - __in_z LPCWSTR wzCharSet - ) -{ - LPCWSTR wzSource = wzString; - LPCWSTR wzSearch = NULL; - SIZE_T cchSourceIndex = 0; - - // Walk through wzString (the source string) one character at a time - while (*wzSource) - { - cchSourceIndex = 0; - wzSearch = wzCharSet; - - // Look ahead in the source string until we get a full match or we hit the end of the source - while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch)) - { - ++cchSourceIndex; - ++wzSearch; - } - - // If we found it, return the point that we found it at - if (L'\0' == *wzSearch) - { - return wzSource; - } - - // Walk ahead one character - ++wzSource; - } - - return NULL; -} - -/**************************************************************************** -StrStringToInt16 - converts a string to a signed 16-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out SHORT* psOut - ) -{ - HRESULT hr = S_OK; - LONGLONG ll = 0; - - hr = StrStringToInt64(wzIn, cchIn, &ll); - StrExitOnFailure(hr, "Failed to parse int64."); - - if (SHORT_MAX < ll || SHORT_MIN > ll) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *psOut = (SHORT)ll; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUInt16 - converts a string to an unsigned 16-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToUInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out USHORT* pusOut - ) -{ - HRESULT hr = S_OK; - ULONGLONG ull = 0; - - hr = StrStringToUInt64(wzIn, cchIn, &ull); - StrExitOnFailure(hr, "Failed to parse uint64."); - - if (USHORT_MAX < ull) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *pusOut = (USHORT)ull; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToInt32 - converts a string to a signed 32-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out INT* piOut - ) -{ - HRESULT hr = S_OK; - LONGLONG ll = 0; - - hr = StrStringToInt64(wzIn, cchIn, &ll); - StrExitOnFailure(hr, "Failed to parse int64."); - - if (INT_MAX < ll || INT_MIN > ll) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *piOut = (INT)ll; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUInt32 - converts a string to an unsigned 32-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToUInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out UINT* puiOut - ) -{ - HRESULT hr = S_OK; - ULONGLONG ull = 0; - - hr = StrStringToUInt64(wzIn, cchIn, &ull); - StrExitOnFailure(hr, "Failed to parse uint64."); - - if (UINT_MAX < ull) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *puiOut = (UINT)ull; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToInt64 - converts a string to a signed 64-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out LONGLONG* pllOut - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - INT iSign = 1; - INT nDigit = 0; - LARGE_INTEGER liValue = { }; - size_t cchString = 0; - - // get string length if not provided - if (0 >= cchIn) - { - hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); - StrExitOnRootFailure(hr, "Failed to get length of string."); - - cchIn = (DWORD)cchString; - if (0 >= cchIn) - { - ExitFunction1(hr = E_INVALIDARG); - } - } - - // check sign - if (L'-' == wzIn[0]) - { - if (1 >= cchIn) - { - ExitFunction1(hr = E_INVALIDARG); - } - i = 1; - iSign = -1; - } - - // read digits - while (i < cchIn) - { - nDigit = wzIn[i] - L'0'; - if (0 > nDigit || 9 < nDigit) - { - ExitFunction1(hr = E_INVALIDARG); - } - liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign; - - if ((liValue.HighPart ^ iSign) & INT_MIN) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - ++i; - } - - *pllOut = liValue.QuadPart; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUInt64 - converts a string to an unsigned 64-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToUInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out ULONGLONG* pullOut - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - DWORD nDigit = 0; - ULONGLONG ullValue = 0; - ULONGLONG ull = 0; - size_t cchString = 0; - - // get string length if not provided - if (0 >= cchIn) - { - hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); - StrExitOnRootFailure(hr, "Failed to get length of string."); - - cchIn = (DWORD)cchString; - if (0 >= cchIn) - { - ExitFunction1(hr = E_INVALIDARG); - } - } - - // read digits - while (i < cchIn) - { - nDigit = wzIn[i] - L'0'; - if (9 < nDigit) - { - ExitFunction1(hr = E_INVALIDARG); - } - ull = (ULONGLONG)(ullValue * 10 + nDigit); - - if (ull < ullValue) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - ullValue = ull; - ++i; - } - - *pullOut = ullValue; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUpper - alters the given string in-place to be entirely uppercase - -****************************************************************************/ -void DAPI StrStringToUpper( - __inout_z LPWSTR wzIn - ) -{ - ::CharUpperBuffW(wzIn, lstrlenW(wzIn)); -} - -/**************************************************************************** -StrStringToLower - alters the given string in-place to be entirely lowercase - -****************************************************************************/ -void DAPI StrStringToLower( - __inout_z LPWSTR wzIn - ) -{ - ::CharLowerBuffW(wzIn, lstrlenW(wzIn)); -} - -/**************************************************************************** -StrAllocStringToUpperInvariant - creates an upper-case copy of a string. - -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocStringToUpperInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE); -} - -/**************************************************************************** -StrAllocStringToLowerInvariant - creates an lower-case copy of a string. - -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocStringToLowerInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE); -} - -/**************************************************************************** -StrArrayAllocString - Allocates a string array. - -****************************************************************************/ -extern "C" HRESULT DAPI StrArrayAllocString( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - HRESULT hr = S_OK; - UINT cNewStrArray; - - hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray); - StrExitOnFailure(hr, "Failed to increment the string array element count."); - - hr = MemEnsureArraySize(reinterpret_cast(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE); - StrExitOnFailure(hr, "Failed to allocate memory for the string array."); - - hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource); - StrExitOnFailure(hr, "Failed to allocate and assign the string."); - - *pcStrArray = cNewStrArray; - -LExit: - return hr; -} - -/**************************************************************************** -StrArrayFree - Frees a string array. - -Use ReleaseNullStrArray to nullify the arguments. - -****************************************************************************/ -extern "C" HRESULT DAPI StrArrayFree( - __in_ecount(cStrArray) LPWSTR *rgsczStrArray, - __in UINT cStrArray - ) -{ - HRESULT hr = S_OK; - - for (UINT i = 0; i < cStrArray; ++i) - { - if (NULL != rgsczStrArray[i]) - { - hr = StrFree(rgsczStrArray[i]); - StrExitOnFailure(hr, "Failed to free the string at index %u.", i); - } - } - - hr = MemFree(rgsczStrArray); - StrExitOnFailure(hr, "Failed to free memory for the string array."); - -LExit: - return hr; -} - -/**************************************************************************** -StrSplitAllocArray - Splits a string into an array. - -****************************************************************************/ -extern "C" HRESULT DAPI StrSplitAllocArray( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzDelim - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCopy = NULL; - LPWSTR wzContext = NULL; - - // Copy wzSource so it is not modified. - hr = StrAllocString(&sczCopy, wzSource, 0); - StrExitOnFailure(hr, "Failed to copy the source string."); - - for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) - { - hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0); - StrExitOnFailure(hr, "Failed to add the string to the string array."); - } - -LExit: - ReleaseStr(sczCopy); - - return hr; -} - -/**************************************************************************** -StrAllocStringMapInvariant - helper function for the ToUpper and ToLower. - -Note: Assumes source and destination buffers will be the same. -****************************************************************************/ -static HRESULT StrAllocStringMapInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in DWORD dwMapFlags - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocString(pscz, wzSource, cchSource); - StrExitOnFailure(hr, "Failed to allocate a copy of the source string."); - - if (0 == cchSource) - { - // Need the actual string size for LCMapString. This includes the null-terminator - // but LCMapString doesn't care either way. - hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast(&cchSource)); - StrExitOnRootFailure(hr, "Failed to get the length of the string."); - } - else if (INT_MAX < cchSource) - { - StrExitOnRootFailure(hr = E_INVALIDARG, "Source string is too long: %Iu", cchSource); - } - - // Convert the copy of the string to upper or lower case in-place. - if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, static_cast(cchSource), *pscz, static_cast(cchSource))) - { - StrExitWithLastError(hr, "Failed to convert the string case."); - } - -LExit: - return hr; -} - -/**************************************************************************** -StrSecureZeroString - zeroes out string to the make sure the contents -don't remain in memory. - -****************************************************************************/ -extern "C" DAPI_(HRESULT) StrSecureZeroString( - __in LPWSTR pwz - ) -{ - HRESULT hr = S_OK; - SIZE_T cch; - - if (pwz) - { - cch = MemSize(pwz); - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "Failed to get size of string"); - } - else - { - SecureZeroMemory(pwz, cch); - } - } - -LExit: - return hr; -} - -/**************************************************************************** -StrSecureZeroFreeString - zeroes out string to the make sure the contents -don't remain in memory, then frees the string. - -****************************************************************************/ -extern "C" DAPI_(HRESULT) StrSecureZeroFreeString( - __in LPWSTR pwz - ) -{ - HRESULT hr = S_OK; - - hr = StrSecureZeroString(pwz); - ReleaseStr(pwz); - - return hr; -} diff --git a/src/dutil/svcutil.cpp b/src/dutil/svcutil.cpp deleted file mode 100644 index 1a39bfee..00000000 --- a/src/dutil/svcutil.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define SvcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) -#define SvcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) -#define SvcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) -#define SvcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) -#define SvcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SVCUTIL, e, x, s, __VA_ARGS__) -#define SvcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SVCUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -SvcQueryConfig - queries the configuration of a service - -********************************************************************/ -extern "C" HRESULT DAPI SvcQueryConfig( - __in SC_HANDLE sch, - __out QUERY_SERVICE_CONFIGW** ppConfig - ) -{ - HRESULT hr = S_OK; - QUERY_SERVICE_CONFIGW* pConfig = NULL; - DWORD cbConfig = 0; - - if (!::QueryServiceConfigW(sch, NULL, 0, &cbConfig)) - { - DWORD er = ::GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == er) - { - pConfig = static_cast(MemAlloc(cbConfig, TRUE)); - SvcExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration."); - - if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig)) - { - SvcExitWithLastError(hr, "Failed to read service configuration."); - } - } - else - { - SvcExitOnWin32Error(er, hr, "Failed to query service configuration."); - } - } - - *ppConfig = pConfig; - pConfig = NULL; - -LExit: - ReleaseMem(pConfig); - - return hr; -} diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp deleted file mode 100644 index d200a0ea..00000000 --- a/src/dutil/thmutil.cpp +++ /dev/null @@ -1,5709 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ThmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) -#define ThmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) -#define ThmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) -#define ThmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) -#define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__) -#define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__) - -// from CommCtrl.h -#ifndef BS_COMMANDLINK -#define BS_COMMANDLINK 0x0000000EL -#endif - -#ifndef BCM_SETNOTE -#define BCM_SETNOTE (BCM_FIRST + 0x0009) -#endif - -#ifndef BCM_SETSHIELD -#define BCM_SETSHIELD (BCM_FIRST + 0x000C) -#endif - -#ifndef LWS_NOPREFIX -#define LWS_NOPREFIX 0x0004 -#endif - -const DWORD THEME_INVALID_ID = 0xFFFFFFFF; -const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF; -const DWORD GROW_FONT_INSTANCES = 3; -const DWORD GROW_WINDOW_TEXT = 250; -const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink"; -const LPCWSTR THEME_WC_PANEL = L"ThemePanel"; -const LPCWSTR THEME_WC_STATICOWNERDRAW = L"ThemeStaticOwnerDraw"; - -static Gdiplus::GdiplusStartupInput vgsi; -static Gdiplus::GdiplusStartupOutput vgso = { }; -static ULONG_PTR vgdiToken = 0; -static ULONG_PTR vgdiHookToken = 0; -static HMODULE vhHyperlinkRegisteredModule = NULL; -static HMODULE vhPanelRegisteredModule = NULL; -static HMODULE vhStaticOwnerDrawRegisteredModule = NULL; -static WNDPROC vpfnStaticOwnerDrawBaseWndProc = NULL; -static HMODULE vhModuleMsftEdit = NULL; -static HMODULE vhModuleRichEd = NULL; -static HCURSOR vhCursorHand = NULL; - -enum INTERNAL_CONTROL_STYLE -{ - INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001, - INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002, - INTERNAL_CONTROL_STYLE_DISABLED = 0x0004, - INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008, - INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010, -}; - -struct MEMBUFFER_FOR_RICHEDIT -{ - BYTE* rgbData; - DWORD cbData; - - DWORD iData; -}; - - -// prototypes -static HRESULT RegisterWindowClasses( - __in_opt HMODULE hModule - ); -static HRESULT ParseTheme( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMDocument* pixd, - __out THEME** ppTheme - ); -static HRESULT ParseImage( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HBITMAP* phImage - ); -static HRESULT ParseIcon( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HICON* phIcon - ); -static HRESULT ParseWindow( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ); -static HRESULT GetFontColor( - __in IXMLDOMNode* pixn, - __in_z LPCWSTR wzAttributeName, - __out COLORREF* pColorRef, - __out DWORD* pdwSystemColor - ); -static HRESULT ParseFonts( - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ); -static HRESULT ParsePages( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ); -static HRESULT ParseImageLists( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ); -static HRESULT ParseControls( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_opt THEME_PAGE* pPage - ); -static HRESULT ParseControl( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in BOOL fSkipDimensions, - __in_opt THEME_PAGE* pPage - ); -static HRESULT ParseActions( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ); -static HRESULT ParseColumns( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ); -static HRESULT ParseRadioButtons( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in THEME_PAGE* pPage - ); -static HRESULT ParseTabs( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ); -static HRESULT ParseText( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren -); -static HRESULT ParseTooltips( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren - ); -static HRESULT ParseNotes( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __out BOOL* pfAnyChildren - ); -static HRESULT StopBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ); -static HRESULT StartBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ); -static HRESULT EnsureFontInstance( - __in THEME* pTheme, - __in THEME_FONT* pFont, - __out THEME_FONT_INSTANCE** ppFontInstance - ); -static HRESULT FindImageList( - __in THEME* pTheme, - __in_z LPCWSTR wzImageListName, - __out HIMAGELIST *phImageList - ); -static HRESULT LoadControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ); -static HRESULT ShowControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId, - __out_opt HWND* phwndFocus - ); -static HRESULT ShowControls( - __in THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId - ); -static HRESULT DrawButton( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static void DrawControlText( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl, - __in BOOL fCentered, - __in BOOL fDrawFocusRect - ); -static HRESULT DrawHyperlink( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static HRESULT DrawImage( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static HRESULT DrawProgressBar( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static BOOL DrawHoverControl( - __in THEME* pTheme, - __in BOOL fHover - ); -static DWORD CALLBACK RichEditStreamFromFileHandleCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG *pcb - ); -static DWORD CALLBACK RichEditStreamFromMemoryCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG *pcb - ); -static void FreeFontInstance( - __in THEME_FONT_INSTANCE* pFontInstance - ); -static void FreeFont( - __in THEME_FONT* pFont - ); -static void FreePage( - __in THEME_PAGE* pPage - ); -static void FreeControl( - __in THEME_CONTROL* pControl - ); -static void FreeConditionalText( - __in THEME_CONDITIONAL_TEXT* pConditionalText - ); -static void FreeImageList( - __in THEME_IMAGELIST* pImageList - ); -static void FreeAction( - __in THEME_ACTION* pAction - ); -static void FreeColumn( - __in THEME_COLUMN* pColumn - ); -static void FreeTab( - __in THEME_TAB* pTab - ); -static void CALLBACK OnBillboardTimer( - __in THEME* pTheme, - __in HWND hwnd, - __in UINT_PTR idEvent - ); -static void OnBrowseDirectory( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_ACTION* pAction - ); -static BOOL OnButtonClicked( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_CONTROL* pControl - ); -static BOOL OnDpiChanged( - __in THEME* pTheme, - __in WPARAM wParam, - __in LPARAM lParam - ); -static void OnNcCreate( - __in THEME* pTheme, - __in HWND hWnd, - __in LPARAM lParam - ); -static HRESULT OnRichEditEnLink( - __in LPARAM lParam, - __in HWND hWndRichEdit, - __in HWND hWnd - ); -static BOOL ControlIsType( - __in const THEME* pTheme, - __in DWORD dwControl, - __in THEME_CONTROL_TYPE type - ); -static const THEME_CONTROL* FindControlFromHWnd( - __in const THEME* pTheme, - __in HWND hWnd, - __in_opt const THEME_CONTROL* pParentControl = NULL - ); -static void GetControlDimensions( - __in const THEME_CONTROL* pControl, - __in const RECT* prcParent, - __out int* piWidth, - __out int* piHeight, - __out int* piX, - __out int* piY - ); -// Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns -// calculates final width of each column (storing result in each column's nWidth value) -static HRESULT SizeListViewColumns( - __inout THEME_CONTROL* pControl - ); -static LRESULT CALLBACK ControlGroupDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static LRESULT CALLBACK PanelWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static LRESULT CALLBACK StaticOwnerDrawWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static HRESULT LocalizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const WIX_LOCALIZATION *pWixLoc - ); -static HRESULT LocalizeControl( - __in THEME_CONTROL* pControl, - __in const WIX_LOCALIZATION *pWixLoc - ); -static HRESULT LoadControlsString( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in HMODULE hResModule - ); -static HRESULT LoadControlString( - __in THEME_CONTROL* pControl, - __in HMODULE hResModule - ); -static void ResizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const RECT* prcParent - ); -static void ResizeControl( - __in THEME_CONTROL* pControl, - __in const RECT* prcParent - ); -static void ScaleThemeFromWindow( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y - ); -static void ScaleTheme( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle - ); -static void ScaleControls( - __in THEME* pTheme, - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in UINT nDpi - ); -static void ScaleControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in UINT nDpi - ); -static void GetControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __out DWORD** ppcControls, - __out THEME_CONTROL*** pprgControls - ); -static void GetControls( - __in const THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __out DWORD& cControls, - __out THEME_CONTROL*& rgControls - ); -static void UnloadControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls - ); - - -// Public functions. - -DAPI_(HRESULT) ThemeInitialize( - __in_opt HMODULE hModule - ) -{ - HRESULT hr = S_OK; - INITCOMMONCONTROLSEX icex = { }; - - DpiuInitialize(); - - hr = XmlInitialize(); - ThmExitOnFailure(hr, "Failed to initialize XML."); - - hr = RegisterWindowClasses(hModule); - ThmExitOnFailure(hr, "Failed to register theme window classes."); - - // Initialize GDI+ and common controls. - vgsi.SuppressBackgroundThread = TRUE; - - hr = GdipInitialize(&vgsi, &vgdiToken, &vgso); - ThmExitOnFailure(hr, "Failed to initialize GDI+."); - - icex.dwSize = sizeof(INITCOMMONCONTROLSEX); - icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS; - ::InitCommonControlsEx(&icex); - - (*vgso.NotificationHook)(&vgdiHookToken); - -LExit: - return hr; -} - - -DAPI_(void) ThemeUninitialize() -{ - if (vhModuleMsftEdit) - { - ::FreeLibrary(vhModuleMsftEdit); - vhModuleMsftEdit = NULL; - } - - if (vhModuleRichEd) - { - ::FreeLibrary(vhModuleRichEd); - vhModuleRichEd = NULL; - } - - if (vhHyperlinkRegisteredModule) - { - ::UnregisterClassW(THEME_WC_HYPERLINK, vhHyperlinkRegisteredModule); - vhHyperlinkRegisteredModule = NULL; - } - - if (vhPanelRegisteredModule) - { - ::UnregisterClassW(THEME_WC_PANEL, vhPanelRegisteredModule); - vhPanelRegisteredModule = NULL; - } - - if (vhStaticOwnerDrawRegisteredModule) - { - ::UnregisterClassW(THEME_WC_STATICOWNERDRAW, vhStaticOwnerDrawRegisteredModule); - vhStaticOwnerDrawRegisteredModule = NULL; - vpfnStaticOwnerDrawBaseWndProc = NULL; - } - - if (vgdiToken) - { - GdipUninitialize(vgdiToken); - vgdiToken = 0; - } - - XmlUninitialize(); - DpiuUninitialize(); -} - - -DAPI_(HRESULT) ThemeLoadFromFile( - __in_z LPCWSTR wzThemeFile, - __out THEME** ppTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixd = NULL; - LPWSTR sczRelativePath = NULL; - - hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd); - ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); - - hr = PathGetDirectory(wzThemeFile, &sczRelativePath); - ThmExitOnFailure(hr, "Failed to get relative path from theme file."); - - hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme); - ThmExitOnFailure(hr, "Failed to parse theme."); - -LExit: - ReleaseStr(sczRelativePath); - ReleaseObject(pixd); - - return hr; -} - - -DAPI_(HRESULT) ThemeLoadFromResource( - __in_opt HMODULE hModule, - __in_z LPCSTR szResource, - __out THEME** ppTheme - ) -{ - HRESULT hr = S_OK; - LPVOID pvResource = NULL; - DWORD cbResource = 0; - LPWSTR sczXml = NULL; - IXMLDOMDocument* pixd = NULL; - - hr = ResReadData(hModule, szResource, &pvResource, &cbResource); - ThmExitOnFailure(hr, "Failed to read theme from resource."); - - hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); - ThmExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); - - hr = XmlLoadDocument(sczXml, &pixd); - ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); - - hr = ParseTheme(hModule, NULL, pixd, ppTheme); - ThmExitOnFailure(hr, "Failed to parse theme."); - -LExit: - ReleaseObject(pixd); - ReleaseStr(sczXml); - - return hr; -} - - -DAPI_(void) ThemeFree( - __in THEME* pTheme - ) -{ - if (pTheme) - { - for (DWORD i = 0; i < pTheme->cFonts; ++i) - { - FreeFont(pTheme->rgFonts + i); - } - - for (DWORD i = 0; i < pTheme->cPages; ++i) - { - FreePage(pTheme->rgPages + i); - } - - for (DWORD i = 0; i < pTheme->cImageLists; ++i) - { - FreeImageList(pTheme->rgImageLists + i); - } - - for (DWORD i = 0; i < pTheme->cControls; ++i) - { - FreeControl(pTheme->rgControls + i); - } - - ReleaseMem(pTheme->rgControls); - ReleaseMem(pTheme->rgPages); - ReleaseMem(pTheme->rgFonts); - - if (pTheme->hImage) - { - ::DeleteBitmap(pTheme->hImage); - } - - ReleaseStr(pTheme->sczCaption); - ReleaseMem(pTheme); - } -} - -DAPI_(HRESULT) ThemeRegisterVariableCallbacks( - __in THEME* pTheme, - __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, - __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, - __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, - __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, - __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, - __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, - __in_opt LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); - - pTheme->pfnEvaluateCondition = pfnEvaluateCondition; - pTheme->pfnFormatString = pfnFormatString; - pTheme->pfnGetNumericVariable = pfnGetNumericVariable; - pTheme->pfnSetNumericVariable = pfnSetNumericVariable; - pTheme->pfnGetStringVariable = pfnGetStringVariable; - pTheme->pfnSetStringVariable = pfnSetStringVariable; - pTheme->pvVariableContext = pvContext; - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeCreateParentWindow( - __in THEME* pTheme, - __in DWORD dwExStyle, - __in LPCWSTR szClassName, - __in LPCWSTR szWindowName, - __in DWORD dwStyle, - __in int x, - __in int y, - __in_opt HWND hwndParent, - __in_opt HINSTANCE hInstance, - __in_opt LPVOID lpParam, - __in THEME_WINDOW_INITIAL_POSITION initialPosition, - __out_opt HWND* phWnd - ) -{ - HRESULT hr = S_OK; - DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; - POINT pt = { }; - RECT* pMonitorRect = NULL; - HMENU hMenu = NULL; - HWND hWnd = NULL; - - if (pTheme->hwndParent) - { - ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded."); - } - - if (THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES == initialPosition) - { - pt.x = x; - pt.y = y; - hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); - if (SUCCEEDED(hr)) - { - pMonitorRect = &pMonitorContext->mi.rcWork; - if (pMonitorContext->nDpi != pTheme->nDpi) - { - ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top, dwStyle, NULL != hMenu, dwExStyle); - } - - x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWindowWidth) / 2; - y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nWindowHeight) / 2; - } - else - { - hr = S_OK; - x = CW_USEDEFAULT; - y = CW_USEDEFAULT; - } - } - - hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, hwndParent, hMenu, hInstance, lpParam); - ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window."); - ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE."); - AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window."); - - if (phWnd) - { - *phWnd = hWnd; - } - -LExit: - ReleaseMem(pMonitorContext); - - return hr; -} - - -DAPI_(HRESULT) ThemeLoadControls( - __in THEME* pTheme, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ) -{ - HRESULT hr = S_OK; - - if (!pTheme->hwndParent) - { - ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created."); - } - - hr = LoadControls(pTheme, NULL, rgAssignControlIds, cAssignControlIds); - -LExit: - return hr; -} - - -DAPI_(void) ThemeUnloadControls( - __in THEME* pTheme - ) -{ - UnloadControls(pTheme->cControls, pTheme->rgControls); - - pTheme->hwndHover = NULL; - pTheme->hwndParent = NULL; -} - -DAPI_(HRESULT) ThemeLocalize( - __in THEME *pTheme, - __in const WIX_LOCALIZATION *pWixLoc - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCaption = NULL; - - hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption); - ThmExitOnFailure(hr, "Failed to localize theme caption."); - - if (pTheme->pfnFormatString) - { - hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext); - if (SUCCEEDED(hr)) - { - hr = ThemeUpdateCaption(pTheme, sczCaption); - } - } - - hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc); - -LExit: - ReleaseStr(sczCaption); - - return hr; -} - -/******************************************************************** - ThemeLoadStrings - Loads string resources. - Must be called after loading a theme and before calling - ThemeLoadControls. -*******************************************************************/ -DAPI_(HRESULT) ThemeLoadStrings( - __in THEME* pTheme, - __in HMODULE hResModule - ) -{ - HRESULT hr = S_OK; - ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); - - if (UINT_MAX != pTheme->uStringId) - { - hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption); - ThmExitOnFailure(hr, "Failed to load theme caption."); - } - - hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule); - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeLoadRichEditFromFile( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCWSTR wzFileName, - __in HMODULE hModule - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFile = NULL; - HANDLE hFile = INVALID_HANDLE_VALUE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - hr = PathRelativeToModule(&sczFile, wzFileName, hModule); - ThmExitOnFailure(hr, "Failed to read resource data."); - - hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ThmExitWithLastError(hr, "Failed to open RTF file."); - } - else - { - LONGLONG llRtfSize; - hr = FileSizeByHandle(hFile, &llRtfSize); - if (SUCCEEDED(hr)) - { - ::SendMessageW(hWnd, EM_EXLIMITTEXT, 0, static_cast(llRtfSize)); - } - - EDITSTREAM es = { }; - es.pfnCallback = RichEditStreamFromFileHandleCallback; - es.dwCookie = reinterpret_cast(hFile); - - ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); - hr = es.dwError; - ThmExitOnFailure(hr, "Failed to update RTF stream."); - } - -LExit: - ReleaseStr(sczFile); - ReleaseFile(hFile); - - return hr; -} - - -DAPI_(HRESULT) ThemeLoadRichEditFromResource( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return ThemeLoadRichEditFromResourceToHWnd(hWnd, szResourceName, hModule); -} - -DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( - __in HWND hWnd, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ) -{ - HRESULT hr = S_OK; - MEMBUFFER_FOR_RICHEDIT buffer = { }; - EDITSTREAM es = { }; - - hr = ResReadData(hModule, szResourceName, reinterpret_cast(&buffer.rgbData), &buffer.cbData); - ThmExitOnFailure(hr, "Failed to read resource data."); - - es.pfnCallback = RichEditStreamFromMemoryCallback; - es.dwCookie = reinterpret_cast(&buffer); - - ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); - hr = es.dwError; - ThmExitOnFailure(hr, "Failed to update RTF stream."); - -LExit: - return hr; -} - - -DAPI_(BOOL) ThemeHandleKeyboardMessage( - __in_opt THEME* pTheme, - __in HWND /*hWnd*/, - __in MSG* pMsg - ) -{ - return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE; -} - - -extern "C" LRESULT CALLBACK ThemeDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - RECT rcParent = { }; - RECT *pRect = NULL; - - if (pTheme) - { - switch (uMsg) - { - case WM_NCCREATE: - if (pTheme->hwndParent) - { - AssertSz(FALSE, "WM_NCCREATE called multiple times"); - } - else - { - OnNcCreate(pTheme, hWnd, lParam); - } - break; - - case WM_NCHITTEST: - if (pTheme->dwStyle & WS_POPUP) - { - return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control. - } - break; - - case WM_DPICHANGED: - if (OnDpiChanged(pTheme, wParam, lParam)) - { - return 0; - } - break; - - case WM_SIZING: - if (pTheme->fAutoResize) - { - pRect = reinterpret_cast(lParam); - if (pRect->right - pRect->left < pTheme->nMinimumWidth) - { - if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) - { - pRect->left = pRect->right - pTheme->nMinimumWidth; - } - else - { - pRect->right = pRect->left + pTheme->nMinimumWidth; - } - } - if (pRect->bottom - pRect->top < pTheme->nMinimumHeight) - { - if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT) - { - pRect->bottom = pRect->top + pTheme->nMinimumHeight; - } - else - { - pRect->top = pRect->bottom - pTheme->nMinimumHeight; - } - } - - return TRUE; - } - break; - - case WM_SIZE: - if (pTheme->fAutoResize || pTheme->fForceResize) - { - pTheme->fForceResize = FALSE; - ::GetClientRect(pTheme->hwndParent, &rcParent); - ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent); - return 0; - } - break; - } - } - - return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); -} - - -DAPI_(void) ThemeGetPageIds( - __in const THEME* pTheme, - __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, - __inout_ecount(cGetPages) DWORD* rgdwPageIds, - __in DWORD cGetPages - ) -{ - for (DWORD i = 0; i < cGetPages; ++i) - { - LPCWSTR wzFindName = rgwzFindNames[i]; - for (DWORD j = 0; j < pTheme->cPages; ++j) - { - LPCWSTR wzPageName = pTheme->rgPages[j].sczName; - if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1)) - { - rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid). - break; - } - } - } -} - - -DAPI_(THEME_PAGE*) ThemeGetPage( - __in const THEME* pTheme, - __in DWORD dwPage - ) -{ - DWORD iPage = dwPage - 1; - THEME_PAGE* pPage = NULL; - - if (iPage < pTheme->cPages) - { - pPage = pTheme->rgPages + iPage; - } - - return pPage; -} - - -DAPI_(HRESULT) ThemeShowPage( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow - ) -{ - return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT); -} - - -DAPI_(HRESULT) ThemeShowPageEx( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow, - __in THEME_SHOW_PAGE_REASON reason - ) -{ - HRESULT hr = S_OK; - BOOL fHide = SW_HIDE == nCmdShow; - BOOL fSaveEditboxes = FALSE; - THEME_SAVEDVARIABLE* pSavedVariable = NULL; - THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage); - - if (pPage) - { - if (fHide) - { - switch (reason) - { - case THEME_SHOW_PAGE_REASON_DEFAULT: - // Set the variables in the loop below. - fSaveEditboxes = TRUE; - break; - case THEME_SHOW_PAGE_REASON_CANCEL: - if (pPage->cSavedVariables && pTheme->pfnSetStringVariable) - { - // Best effort to cancel any changes to the variables. - for (DWORD v = 0; v < pPage->cSavedVariables; ++v) - { - pSavedVariable = pPage->rgSavedVariables + v; - if (pSavedVariable->wzName) - { - pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, FALSE, pTheme->pvVariableContext); - } - } - } - break; - } - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason) - { - pPage->cSavedVariables = 0; - if (pPage->rgSavedVariables) - { - SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); - } - } - - pTheme->dwCurrentPageId = 0; - } - else - { - if (THEME_SHOW_PAGE_REASON_REFRESH == reason) - { - fSaveEditboxes = TRUE; - } - else - { - hr = MemEnsureArraySize(reinterpret_cast(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices); - ThmExitOnFailure(hr, "Failed to allocate memory for saved variables."); - - SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); - pPage->cSavedVariables = pPage->cControlIndices; - - // Save the variables in the loop below. - } - - pTheme->dwCurrentPageId = dwPage; - } - } - - hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage); - ThmExitOnFailure(hr, "Failed to show page controls."); - -LExit: - return hr; -} - - -DAPI_(BOOL) ThemeControlExists( - __in const THEME* pTheme, - __in DWORD dwControl - ) -{ - BOOL fExists = FALSE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hWnd) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - fExists = (pControl && hWnd == pControl->hWnd); - } - - return fExists; -} - - -DAPI_(void) ThemeControlEnable( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fEnable - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl) - { - pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED); - ::EnableWindow(hWnd, fEnable); - - if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) - { - ::ShowWindow(hWnd, fEnable ? SW_SHOW : SW_HIDE); - } - } -} - - -DAPI_(BOOL) ThemeControlEnabled( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - return pControl && !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); -} - - -DAPI_(void) ThemeControlElevates( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fElevates - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - ::SendMessageW(hWnd, BCM_SETSHIELD, 0, fElevates); -} - - -DAPI_(void) ThemeShowControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - ::ShowWindow(hWnd, nCmdShow); - - // Save the control's visible state. - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl) - { - pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN); - } -} - - -DAPI_(void) ThemeShowControlEx( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl) - { - ShowControl(pTheme, pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL); - } -} - - -DAPI_(BOOL) ThemeControlVisible( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return ::IsWindowVisible(hWnd); -} - - -DAPI_(BOOL) ThemePostControlMessage( - __in THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (!::PostMessageW(hWnd, Msg, wParam, lParam)) - { - er = ::GetLastError(); - hr = HRESULT_FROM_WIN32(er); - } - - return SUCCEEDED(hr); -} - - -DAPI_(LRESULT) ThemeSendControlMessage( - __in const THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return ::SendMessageW(hWnd, Msg, wParam, lParam); -} - - -DAPI_(HRESULT) ThemeDrawBackground( - __in THEME* pTheme, - __in PAINTSTRUCT* pps - ) -{ - HRESULT hr = S_FALSE; - - if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase) - { - HDC hdcMem = ::CreateCompatibleDC(pps->hdc); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pTheme->hImage)); - DWORD dwSourceWidth = pTheme->nDefaultDpiWidth; - DWORD dwSourceHeight = pTheme->nDefaultDpiHeight; - - ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - - hr = S_OK; - } - - return hr; -} - - -DAPI_(HRESULT) ThemeDrawControl( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis - ) -{ - HRESULT hr = S_OK; - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, pdis->hwndItem); - - AssertSz(pControl, "Expected control window from owner draw window."); - AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window."); - AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width."); - AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height."); - - switch (pControl->type) - { - case THEME_CONTROL_TYPE_BUTTON: - hr = DrawButton(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw button."); - break; - - case THEME_CONTROL_TYPE_HYPERLINK: - hr = DrawHyperlink(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw hyperlink."); - break; - - case THEME_CONTROL_TYPE_IMAGE: - hr = DrawImage(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw image."); - break; - - case THEME_CONTROL_TYPE_PROGRESSBAR: - hr = DrawProgressBar(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw progress bar."); - break; - - default: - hr = E_UNEXPECTED; - ThmExitOnRootFailure(hr, "Did not specify an owner draw control to draw."); - } - -LExit: - return hr; -} - - -DAPI_(BOOL) ThemeHoverControl( - __in THEME* pTheme, - __in HWND hwndParent, - __in HWND hwndControl - ) -{ - BOOL fHovered = FALSE; - if (hwndControl != pTheme->hwndHover) - { - if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) - { - DrawHoverControl(pTheme, FALSE); - } - - pTheme->hwndHover = hwndControl; - - if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) - { - fHovered = DrawHoverControl(pTheme, TRUE); - } - } - - return fHovered; -} - - -DAPI_(BOOL) ThemeIsControlChecked( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return BST_CHECKED == ::SendMessageW(hWnd, BM_GETCHECK, 0, 0); -} - - -DAPI_(BOOL) ThemeSetControlColor( - __in THEME* pTheme, - __in HDC hdc, - __in HWND hWnd, - __out HBRUSH* phBackgroundBrush - ) -{ - THEME_FONT* pFont = NULL; - BOOL fHasBackground = FALSE; - - *phBackgroundBrush = NULL; - - if (hWnd == pTheme->hwndParent) - { - pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId; - } - else - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - pFont = (!pControl || THEME_INVALID_ID == pControl->dwFontId) ? NULL : pTheme->rgFonts + pControl->dwFontId; - } - - if (pFont) - { - if (pFont->hForeground) - { - ::SetTextColor(hdc, pFont->crForeground); - } - - if (pFont->hBackground) - { - ::SetBkColor(hdc, pFont->crBackground); - - *phBackgroundBrush = pFont->hBackground; - fHasBackground = TRUE; - } - else - { - ::SetBkMode(hdc, TRANSPARENT); - *phBackgroundBrush = static_cast(::GetStockObject(NULL_BRUSH)); - fHasBackground = TRUE; - } - } - - return fHasBackground; -} - - -DAPI_(HRESULT) ThemeSetProgressControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwProgressPercentage - ) -{ - HRESULT hr = E_NOTFOUND; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl && THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) - { - DWORD dwCurrentProgress = LOWORD(pControl->dwData); - - if (dwCurrentProgress != dwProgressPercentage) - { - DWORD dwColor = HIWORD(pControl->dwData); - pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor); - - if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW) - { - if (!::InvalidateRect(hWnd, NULL, FALSE)) - { - ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); - } - } - else - { - ::SendMessageW(hWnd, PBM_SETPOS, dwProgressPercentage, 0); - } - - hr = S_OK; - } - else - { - hr = S_FALSE; - } - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeSetProgressControlColor( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwColorIndex - ) -{ - HRESULT hr = S_FALSE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hWnd) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - - // Only set color on owner draw progress bars. - if (pControl && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)) - { - DWORD dwCurrentColor = HIWORD(pControl->dwData); - - if (dwCurrentColor != dwColorIndex) - { - DWORD dwCurrentProgress = LOWORD(pControl->dwData); - pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex); - - if (!::InvalidateRect(hWnd, NULL, FALSE)) - { - ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); - } - - hr = S_OK; - } - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeSetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __in_z_opt LPCWSTR wzText - ) -{ - return ThemeSetTextControlEx(pTheme, dwControl, FALSE, wzText); -} - - -DAPI_(HRESULT) ThemeSetTextControlEx( - __in const THEME* pTheme, - __in DWORD dwControl, - __in BOOL fUpdate, - __in_z_opt LPCWSTR wzText - ) -{ - HRESULT hr = S_OK; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - if (fUpdate) - { - ::ShowWindow(hWnd, SW_HIDE); - } - - if (!::SetWindowTextW(hWnd, wzText)) - { - ThmExitWithLastError(hr, "Failed to set control text."); - } - - if (fUpdate) - { - ::ShowWindow(hWnd, SW_SHOW); - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeGetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __inout_z LPWSTR* psczText - ) -{ - HRESULT hr = S_OK; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - SIZE_T cbSize = 0; - DWORD cchText = 0; - DWORD cchTextRead = 0; - - // Ensure the string has room for at least one character. - hr = StrMaxLength(*psczText, &cbSize); - ThmExitOnFailure(hr, "Failed to get text buffer length."); - - cchText = (DWORD)min(DWORD_MAX, cbSize); - - if (!cchText) - { - cchText = GROW_WINDOW_TEXT; - - hr = StrAlloc(psczText, cchText); - ThmExitOnFailure(hr, "Failed to grow text buffer."); - } - - // Read (and keep growing buffer) until we finally read less than there - // is room in the buffer. - for (;;) - { - cchTextRead = ::GetWindowTextW(hWnd, *psczText, cchText); - if (cchTextRead + 1 < cchText) - { - break; - } - else - { - cchText = cchTextRead + GROW_WINDOW_TEXT; - - hr = StrAlloc(psczText, cchText); - ThmExitOnFailure(hr, "Failed to grow text buffer again."); - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeUpdateCaption( - __in THEME* pTheme, - __in_z LPCWSTR wzCaption - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0); - ThmExitOnFailure(hr, "Failed to update theme caption."); - -LExit: - return hr; -} - - -DAPI_(void) ThemeSetFocus( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hwndFocus = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hwndFocus && !ThemeControlEnabled(pTheme, dwControl)) - { - hwndFocus = ::GetNextDlgTabItem(pTheme->hwndParent, hwndFocus, FALSE); - } - - if (hwndFocus) - { - ::SetFocus(hwndFocus); - } -} - - -DAPI_(void) ThemeShowChild( - __in THEME* pTheme, - __in THEME_CONTROL* pParentControl, - __in DWORD dwIndex - ) -{ - // show one child, hide the rest - for (DWORD i = 0; i < pParentControl->cControls; ++i) - { - THEME_CONTROL* pControl = pParentControl->rgControls + i; - ShowControl(pTheme, pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL); - } -} - - -// Internal functions. - -static HRESULT RegisterWindowClasses( - __in_opt HMODULE hModule - ) -{ - HRESULT hr = S_OK; - WNDCLASSW wcHyperlink = { }; - WNDCLASSW wcPanel = { }; - WNDCLASSW wcStaticOwnerDraw = { }; - WNDPROC pfnStaticOwnerDrawBaseWndProc = NULL; - - vhCursorHand = ::LoadCursorA(NULL, IDC_HAND); - - // Base the theme hyperlink class on a button but give it the "hand" icon. - if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink)) - { - ThmExitWithLastError(hr, "Failed to get button window class."); - } - - wcHyperlink.lpszClassName = THEME_WC_HYPERLINK; -#pragma prefast(push) -#pragma prefast(disable:25068) - wcHyperlink.hCursor = vhCursorHand; -#pragma prefast(pop) - - if (!::RegisterClassW(&wcHyperlink)) - { - ThmExitWithLastError(hr, "Failed to get button window class."); - } - vhHyperlinkRegisteredModule = hModule; - - // Panel is its own do-nothing class. - wcPanel.lpfnWndProc = PanelWndProc; - wcPanel.hInstance = hModule; - wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW); - wcPanel.lpszClassName = THEME_WC_PANEL; - if (!::RegisterClassW(&wcPanel)) - { - ThmExitWithLastError(hr, "Failed to register window."); - } - vhPanelRegisteredModule = hModule; - - if (!::GetClassInfoW(NULL, WC_STATICW, &wcStaticOwnerDraw)) - { - ThmExitWithLastError(hr, "Failed to get static window class."); - } - - pfnStaticOwnerDrawBaseWndProc = wcStaticOwnerDraw.lpfnWndProc; - wcStaticOwnerDraw.lpfnWndProc = StaticOwnerDrawWndProc; - wcStaticOwnerDraw.hInstance = hModule; - wcStaticOwnerDraw.lpszClassName = THEME_WC_STATICOWNERDRAW; - if (!::RegisterClassW(&wcStaticOwnerDraw)) - { - ThmExitWithLastError(hr, "Failed to register OwnerDraw window class."); - } - vhStaticOwnerDrawRegisteredModule = hModule; - vpfnStaticOwnerDrawBaseWndProc = pfnStaticOwnerDrawBaseWndProc; - - -LExit: - return hr; -} - -static HRESULT ParseTheme( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMDocument* pixd, - __out THEME** ppTheme - ) -{ - static WORD wThemeId = 0; - - HRESULT hr = S_OK; - THEME* pTheme = NULL; - IXMLDOMElement *pThemeElement = NULL; - - hr = pixd->get_documentElement(&pThemeElement); - ThmExitOnFailure(hr, "Failed to get theme element."); - - pTheme = static_cast(MemAlloc(sizeof(THEME), TRUE)); - ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); - - pTheme->wId = ++wThemeId; - pTheme->nDpi = USER_DEFAULT_SCREEN_DPI; - - // Parse the optional background resource image. - hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); - ThmExitOnFailure(hr, "Failed while parsing theme image."); - - // Parse the fonts. - hr = ParseFonts(pThemeElement, pTheme); - ThmExitOnFailure(hr, "Failed to parse theme fonts."); - - // Parse the window element. - hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme); - ThmExitOnFailure(hr, "Failed to parse theme window element."); - - *ppTheme = pTheme; - pTheme = NULL; - -LExit: - ReleaseObject(pThemeElement); - - if (pTheme) - { - ThemeFree(pTheme); - } - - return hr; -} - -static HRESULT ParseImage( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HBITMAP* phImage - ) -{ - HRESULT hr = S_OK; - BSTR bstr = NULL; - LPWSTR sczImageFile = NULL; - int iResourceId = 0; - Gdiplus::Bitmap* pBitmap = NULL; - *phImage = NULL; - - hr = XmlGetAttribute(pElement, L"ImageResource", &bstr); - ThmExitOnFailure(hr, "Failed to get image resource attribute."); - - if (S_OK == hr) - { - iResourceId = wcstol(bstr, NULL, 10); - - hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(iResourceId), &pBitmap); - // Don't fail. - } - - ReleaseNullBSTR(bstr); - - // Parse the optional background image from a given file. - if (!pBitmap) - { - hr = XmlGetAttribute(pElement, L"ImageFile", &bstr); - ThmExitOnFailure(hr, "Failed to get image file attribute."); - - if (S_OK == hr) - { - if (wzRelativePath) - { - hr = PathConcat(wzRelativePath, bstr, &sczImageFile); - ThmExitOnFailure(hr, "Failed to combine image file path."); - } - else - { - hr = PathRelativeToModule(&sczImageFile, bstr, hModule); - ThmExitOnFailure(hr, "Failed to get image filename."); - } - - hr = GdipBitmapFromFile(sczImageFile, &pBitmap); - // Don't fail. - } - } - - // If there is an image, convert it into a bitmap handle. - if (pBitmap) - { - Gdiplus::Color black; - Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage); - ThmExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP."); - } - - hr = S_OK; - -LExit: - if (pBitmap) - { - delete pBitmap; - } - - ReleaseStr(sczImageFile); - ReleaseBSTR(bstr); - - return hr; -} - - -static HRESULT ParseIcon( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HICON* phIcon - ) -{ - HRESULT hr = S_OK; - BSTR bstr = NULL; - LPWSTR sczImageFile = NULL; - int iResourceId = 0; - *phIcon = NULL; - - hr = XmlGetAttribute(pElement, L"IconResource", &bstr); - ThmExitOnFailure(hr, "Failed to get icon resource attribute."); - - if (S_OK == hr) - { - iResourceId = wcstol(bstr, NULL, 10); - - *phIcon = reinterpret_cast(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); - ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon."); - } - else - { - ReleaseNullBSTR(bstr); - - hr = XmlGetAttribute(pElement, L"IconFile", &bstr); - ThmExitOnFailure(hr, "Failed to get icon file attribute."); - - if (S_OK == hr) - { - if (wzRelativePath) - { - hr = PathConcat(wzRelativePath, bstr, &sczImageFile); - ThmExitOnFailure(hr, "Failed to combine image file path."); - } - else - { - hr = PathRelativeToModule(&sczImageFile, bstr, hModule); - ThmExitOnFailure(hr, "Failed to get image filename."); - } - - *phIcon = reinterpret_cast(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)); - ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile); - } - } - -LExit: - ReleaseStr(sczImageFile); - ReleaseBSTR(bstr); - - return hr; -} - - -static HRESULT ParseWindow( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - DWORD dwValue = 0; - BSTR bstr = NULL; - LPWSTR sczIconFile = NULL; - - hr = XmlSelectSingleNode(pElement, L"Window", &pixn); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find window element."); - - hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get window AutoResize attribute."); - - hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Failed to find window Width attribute."); - } - ThmExitOnFailure(hr, "Failed to get window Width attribute."); - - pTheme->nWidth = pTheme->nDefaultDpiWidth = pTheme->nWindowWidth = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Failed to find window Height attribute."); - } - ThmExitOnFailure(hr, "Failed to get window Height attribute."); - - pTheme->nHeight = pTheme->nDefaultDpiHeight = pTheme->nWindowHeight = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue); - if (S_FALSE == hr) - { - dwValue = 0; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); - - pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue); - if (S_FALSE == hr) - { - dwValue = 0; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); - - pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Failed to find window FontId attribute."); - } - ThmExitOnFailure(hr, "Failed to get window FontId attribute."); - - // Get the optional window icon from a resource. - hr = XmlGetAttribute(pixn, L"IconResource", &bstr); - ThmExitOnFailure(hr, "Failed to get window IconResource attribute."); - - if (S_OK == hr) - { - pTheme->hIcon = ::LoadIconW(hModule, bstr); - ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource."); - - ReleaseNullBSTR(bstr); - } - - // Get the optional window icon from a file. - hr = XmlGetAttribute(pixn, L"IconFile", &bstr); - ThmExitOnFailure(hr, "Failed to get window IconFile attribute."); - - if (S_OK == hr) - { - if (wzRelativePath) - { - hr = PathConcat(wzRelativePath, bstr, &sczIconFile); - ThmExitOnFailure(hr, "Failed to combine icon file path."); - } - else - { - hr = PathRelativeToModule(&sczIconFile, bstr, hModule); - ThmExitOnFailure(hr, "Failed to get icon filename."); - } - - pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); - ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr); - - ReleaseNullBSTR(bstr); - } - - hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pTheme->nSourceX)); - if (S_FALSE == hr) - { - pTheme->nSourceX = -1; - } - ThmExitOnFailure(hr, "Failed to get window SourceX attribute."); - - hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pTheme->nSourceY)); - if (S_FALSE == hr) - { - pTheme->nSourceY = -1; - } - ThmExitOnFailure(hr, "Failed to get window SourceY attribute."); - - // Parse the optional window style. - hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle); - ThmExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute."); - - if (S_FALSE == hr) - { - pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU; - pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED; - } - - hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pTheme->uStringId)); - ThmExitOnFailure(hr, "Failed to get window StringId attribute."); - - if (S_FALSE == hr) - { - pTheme->uStringId = UINT_MAX; - - hr = XmlGetAttribute(pixn, L"Caption", &bstr); - ThmExitOnFailure(hr, "Failed to get window Caption attribute."); - - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute."); - } - - hr = StrAllocString(&pTheme->sczCaption, bstr, 0); - ThmExitOnFailure(hr, "Failed to copy window Caption attribute."); - } - - // Parse any image lists. - hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme); - ThmExitOnFailure(hr, "Failed to parse image lists."); - - // Parse the pages. - hr = ParsePages(hModule, wzRelativePath, pixn, pTheme); - ThmExitOnFailure(hr, "Failed to parse theme pages."); - - // Parse the non-paged controls. - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL); - ThmExitOnFailure(hr, "Failed to parse theme controls."); - -LExit: - ReleaseStr(sczIconFile); - ReleaseBSTR(bstr); - ReleaseObject(pixn); - - return hr; -} - - -static HRESULT ParseFonts( - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixn = NULL; - BSTR bstrName = NULL; - DWORD dwId = 0; - COLORREF crForeground = THEME_INVISIBLE_COLORREF; - COLORREF crBackground = THEME_INVISIBLE_COLORREF; - DWORD dwSystemForegroundColor = FALSE; - DWORD dwSystemBackgroundColor = FALSE; - - hr = XmlSelectNodes(pElement, L"Font", &pixnl); - ThmExitOnFailure(hr, "Failed to find font elements."); - - hr = pixnl->get_length(reinterpret_cast(&pTheme->cFonts)); - ThmExitOnFailure(hr, "Failed to count the number of theme fonts."); - - if (!pTheme->cFonts) - { - ExitFunction1(hr = S_OK); - } - - pTheme->rgFonts = static_cast(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE)); - ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) - { - hr = XmlGetAttributeNumber(pixn, L"Id", &dwId); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find font id."); - - if (pTheme->cFonts <= dwId) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Invalid theme font id."); - } - - THEME_FONT* pFont = pTheme->rgFonts + dwId; - if (pFont->cFontInstances) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Theme font id duplicated."); - } - - pFont->lfQuality = CLEARTYPE_QUALITY; - - hr = XmlGetText(pixn, &bstrName); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to get font name."); - - hr = StrAllocString(&pFont->sczFaceName, bstrName, 0); - ThmExitOnFailure(hr, "Failed to copy font name."); - - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pFont->lfHeight)); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find font height attribute."); - - hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&pFont->lfWeight)); - if (S_FALSE == hr) - { - pFont->lfWeight = FW_DONTCARE; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to find font weight attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&pFont->lfUnderline)); - if (E_NOTFOUND == hr) - { - pFont->lfUnderline = FALSE; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to find font underline attribute."); - - hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor); - ThmExitOnFailure(hr, "Failed to find font foreground color."); - - hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor); - ThmExitOnFailure(hr, "Failed to find font background color."); - - pFont->crForeground = crForeground; - if (THEME_INVISIBLE_COLORREF != pFont->crForeground) - { - pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground); - ThmExitOnNull(pFont->hForeground, hr, E_OUTOFMEMORY, "Failed to create text foreground brush."); - } - - pFont->crBackground = crBackground; - if (THEME_INVISIBLE_COLORREF != pFont->crBackground) - { - pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground); - ThmExitOnNull(pFont->hBackground, hr, E_OUTOFMEMORY, "Failed to create text background brush."); - } - - ReleaseNullBSTR(bstrName); - ReleaseNullObject(pixn); - } - ThmExitOnFailure(hr, "Failed to enumerate all fonts."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrName); - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - - -static HRESULT GetFontColor( - __in IXMLDOMNode* pixn, - __in_z LPCWSTR wzAttributeName, - __out COLORREF* pColorRef, - __out DWORD* pdwSystemColor - ) -{ - HRESULT hr = S_OK; - BSTR bstr = NULL; - - *pdwSystemColor = 0; - - hr = XmlGetAttribute(pixn, wzAttributeName, &bstr); - if (S_FALSE == hr) - { - *pColorRef = THEME_INVISIBLE_COLORREF; - ExitFunction1(hr = S_OK); - } - ThmExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName); - - if (pdwSystemColor) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1)) - { - *pdwSystemColor = COLOR_BTNFACE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1)) - { - *pdwSystemColor = COLOR_BTNTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1)) - { - *pdwSystemColor = COLOR_GRAYTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1)) - { - *pdwSystemColor = COLOR_HIGHLIGHT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1)) - { - *pdwSystemColor = COLOR_HIGHLIGHTTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1)) - { - *pdwSystemColor = COLOR_HOTLIGHT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1)) - { - *pdwSystemColor = COLOR_WINDOW; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1)) - { - *pdwSystemColor = COLOR_WINDOWTEXT; - } - else - { - *pColorRef = wcstoul(bstr, NULL, 16); - } - - if (*pdwSystemColor) - { - *pColorRef = ::GetSysColor(*pdwSystemColor); - } - } - -LExit: - ReleaseBSTR(bstr); - - return hr; -} - -static HRESULT ParsePages( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixn = NULL; - BSTR bstrType = NULL; - THEME_PAGE* pPage = NULL; - DWORD iPage = 0; - - hr = XmlSelectNodes(pElement, L"Page", &pixnl); - ThmExitOnFailure(hr, "Failed to find page elements."); - - hr = pixnl->get_length(reinterpret_cast(&pTheme->cPages)); - ThmExitOnFailure(hr, "Failed to count the number of theme pages."); - - if (!pTheme->cPages) - { - ExitFunction1(hr = S_OK); - } - - pTheme->rgPages = static_cast(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE)); - ThmExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) - { - pPage = pTheme->rgPages + iPage; - - pPage->wId = static_cast(iPage + 1); - - hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying page Name."); - - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage); - ThmExitOnFailure(hr, "Failed to parse page controls."); - - ++iPage; - - ReleaseNullBSTR(bstrType); - ReleaseNullObject(pixn); - } - ThmExitOnFailure(hr, "Failed to enumerate all pages."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrType); - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - - -static HRESULT ParseImageLists( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnlImageLists = NULL; - IXMLDOMNode* pixnImageList = NULL; - IXMLDOMNodeList* pixnlImages = NULL; - IXMLDOMNode* pixnImage = NULL; - DWORD dwImageListIndex = 0; - DWORD dwImageCount = 0; - HBITMAP hBitmap = NULL; - BITMAP bm = { }; - BSTR bstr = NULL; - DWORD i = 0; - int iRetVal = 0; - - hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists); - ThmExitOnFailure(hr, "Failed to find ImageList elements."); - - hr = pixnlImageLists->get_length(reinterpret_cast(&pTheme->cImageLists)); - ThmExitOnFailure(hr, "Failed to count the number of image lists."); - - if (!pTheme->cImageLists) - { - ExitFunction1(hr = S_OK); - } - - pTheme->rgImageLists = static_cast(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE)); - ThmExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists."); - - while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL))) - { - hr = XmlGetAttribute(pixnImageList, L"Name", &bstr); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find ImageList/@Name attribute."); - - hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0); - ThmExitOnFailure(hr, "Failed to make copy of ImageList name."); - - hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages); - ThmExitOnFailure(hr, "Failed to select child Image nodes."); - - hr = pixnlImages->get_length(reinterpret_cast(&dwImageCount)); - ThmExitOnFailure(hr, "Failed to count the number of images in list."); - - if (0 < dwImageCount) - { - i = 0; - while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL))) - { - if (hBitmap) - { - ::DeleteObject(hBitmap); - hBitmap = NULL; - } - hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap); - ThmExitOnFailure(hr, "Failed to parse image: %u", i); - - if (0 == i) - { - ::GetObjectW(hBitmap, sizeof(BITMAP), &bm); - - pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0); - ThmExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list."); - } - - iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL); - if (-1 == iRetVal) - { - ThmExitWithLastError(hr, "Failed to add image %u to image list.", i); - } - - ++i; - } - } - ++dwImageListIndex; - } - -LExit: - if (hBitmap) - { - ::DeleteObject(hBitmap); - } - ReleaseBSTR(bstr); - ReleaseObject(pixnlImageLists); - ReleaseObject(pixnImageList); - ReleaseObject(pixnlImages); - ReleaseObject(pixnImage); - - return hr; -} - -static void GetControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __out DWORD** ppcControls, - __out THEME_CONTROL*** pprgControls - ) -{ - if (pParentControl) - { - *ppcControls = &pParentControl->cControls; - *pprgControls = &pParentControl->rgControls; - } - else - { - *ppcControls = &pTheme->cControls; - *pprgControls = &pTheme->rgControls; - } -} - -static void GetControls( - __in const THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __out DWORD& cControls, - __out THEME_CONTROL*& rgControls - ) -{ - if (pParentControl) - { - cControls = pParentControl->cControls; - rgControls = pParentControl->rgControls; - } - else - { - cControls = pTheme->cControls; - rgControls = pTheme->rgControls; - } -} - -static HRESULT ParseControls( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_opt THEME_PAGE* pPage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixn = NULL; - BSTR bstrType = NULL; - DWORD cNewControls = 0; - DWORD iControl = 0; - DWORD iPageControl = 0; - DWORD* pcControls = NULL; - THEME_CONTROL** prgControls = NULL; - - GetControls(pTheme, pParentControl, &pcControls, &prgControls); - - hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage); - ThmExitOnFailure(hr, "Failed to parse radio buttons."); - - hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl); - ThmExitOnFailure(hr, "Failed to find control elements."); - - hr = pixnl->get_length(reinterpret_cast(&cNewControls)); - ThmExitOnFailure(hr, "Failed to count the number of theme controls."); - - if (!cNewControls) - { - ExitFunction1(hr = S_OK); - } - - hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls); - ThmExitOnFailure(hr, "Failed to reallocate theme controls."); - - cNewControls += *pcControls; - - if (pPage) - { - iPageControl = pPage->cControlIndices; - pPage->cControlIndices += cNewControls; - } - - iControl = *pcControls; - *pcControls = cNewControls; - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) - { - THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN; - - if (!bstrType) - { - hr = E_UNEXPECTED; - ThmExitOnFailure(hr, "Null element encountered!"); - } - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1)) - { - type = THEME_CONTROL_TYPE_BILLBOARD; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1)) - { - type = THEME_CONTROL_TYPE_BUTTON; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1)) - { - type = THEME_CONTROL_TYPE_CHECKBOX; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1)) - { - type = THEME_CONTROL_TYPE_COMBOBOX; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1)) - { - type = THEME_CONTROL_TYPE_COMMANDLINK; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1)) - { - type = THEME_CONTROL_TYPE_EDITBOX; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1)) - { - type = THEME_CONTROL_TYPE_HYPERLINK; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1)) - { - type = THEME_CONTROL_TYPE_HYPERTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1)) - { - type = THEME_CONTROL_TYPE_IMAGE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1)) - { - type = THEME_CONTROL_TYPE_LABEL; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1)) - { - type = THEME_CONTROL_TYPE_LISTVIEW; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1)) - { - type = THEME_CONTROL_TYPE_PANEL; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1)) - { - type = THEME_CONTROL_TYPE_PROGRESSBAR; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1)) - { - type = THEME_CONTROL_TYPE_RICHEDIT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1)) - { - type = THEME_CONTROL_TYPE_STATIC; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1)) - { - type = THEME_CONTROL_TYPE_TAB; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1)) - { - type = THEME_CONTROL_TYPE_TREEVIEW; - } - - if (THEME_CONTROL_TYPE_UNKNOWN != type) - { - THEME_CONTROL* pControl = *prgControls + iControl; - pControl->type = type; - - // billboard children are always the size of the billboard - BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type; - - hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage); - ThmExitOnFailure(hr, "Failed to parse control."); - - if (fBillboardSizing) - { - pControl->nX = pControl->nDefaultDpiX = 0; - pControl->nY = pControl->nDefaultDpiY = 0; - pControl->nWidth = pControl->nDefaultDpiWidth = 0; - pControl->nHeight = pControl->nDefaultDpiHeight = 0; - } - - if (pPage) - { - pControl->wPageId = pPage->wId; - ++iPageControl; - } - - ++iControl; - } - - ReleaseNullBSTR(bstrType); - ReleaseNullObject(pixn); - } - ThmExitOnFailure(hr, "Failed to enumerate all controls."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - - AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls."); - -LExit: - ReleaseBSTR(bstrType); - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - - -static HRESULT ParseControl( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in BOOL fSkipDimensions, - __in_opt THEME_PAGE* pPage - ) -{ - HRESULT hr = S_OK; - DWORD dwValue = 0; - BOOL fValue = FALSE; - BSTR bstrText = NULL; - BOOL fAnyTextChildren = FALSE; - BOOL fAnyNoteChildren = FALSE; - - hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying control Name attribute."); - - hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying control EnableCondition attribute."); - - hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying control VisibleCondition attribute."); - - if (!fSkipDimensions) - { - hr = XmlGetAttributeNumber(pixn, L"X", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control X attribute."); - - pControl->nX = pControl->nDefaultDpiX = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Y", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control Y attribute."); - - pControl->nY = pControl->nDefaultDpiY = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control Height attribute."); - - pControl->nHeight = pControl->nDefaultDpiHeight = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control Width attribute."); - - pControl->nWidth = pControl->nDefaultDpiWidth = dwValue; - } - - // Parse the optional background resource image. - hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage); - ThmExitOnFailure(hr, "Failed while parsing control image."); - - hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pControl->nSourceX)); - if (S_FALSE == hr) - { - pControl->nSourceX = -1; - } - ThmExitOnFailure(hr, "Failed when querying control SourceX attribute."); - - hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pControl->nSourceY)); - if (S_FALSE == hr) - { - pControl->nSourceY = -1; - } - ThmExitOnFailure(hr, "Failed when querying control SourceY attribute."); - - hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId); - if (S_FALSE == hr) - { - pControl->dwFontId = THEME_INVALID_ID; - } - ThmExitOnFailure(hr, "Failed when querying control FontId attribute."); - - // Parse the optional window style. - hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle); - ThmExitOnFailure(hr, "Failed when querying control HexStyle attribute."); - - // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above. - hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control TabStop attribute."); - - if (fValue) - { - pControl->dwStyle |= WS_TABSTOP; - } - } - - hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control Visible attribute."); - - if (fValue) - { - pControl->dwStyle |= WS_VISIBLE; - } - } - - hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute."); - - if (fValue) - { - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED; - } - } - - hr = XmlGetYesNoAttribute(pixn, L"DisableAutomaticBehavior", &pControl->fDisableVariableFunctionality); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute."); - } - - hr = ParseActions(pixn, pControl); - ThmExitOnFailure(hr, "Failed to parse action nodes of the control."); - - hr = ParseText(pixn, pControl, &fAnyTextChildren); - ThmExitOnFailure(hr, "Failed to parse text nodes of the control."); - - hr = ParseTooltips(pixn, pControl, &fAnyTextChildren); - ThmExitOnFailure(hr, "Failed to parse control Tooltip."); - - if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - hr = ParseNotes(pixn, pControl, &fAnyNoteChildren); - ThmExitOnFailure(hr, "Failed to parse note text nodes of the control."); - } - - if (fAnyTextChildren || fAnyNoteChildren) - { - pControl->uStringId = UINT_MAX; - } - else - { - hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pControl->uStringId)); - ThmExitOnFailure(hr, "Failed when querying control StringId attribute."); - - if (S_FALSE == hr) - { - pControl->uStringId = UINT_MAX; - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type || THEME_CONTROL_TYPE_PANEL == pControl->type) - { - // Billboards and panels have child elements and we don't want to pick up child element text in the parents. - hr = S_OK; - } - else - { - hr = XmlGetText(pixn, &bstrText); - ThmExitOnFailure(hr, "Failed to get control inner text."); - - if (S_OK == hr) - { - hr = StrAllocString(&pControl->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy control text."); - - ReleaseNullBSTR(bstrText); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - } - } - } - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute."); - - pControl->wBillboardInterval = 5000; - hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue); - if (S_OK == hr && dwValue) - { - pControl->wBillboardInterval = static_cast(dwValue & 0xFFFF); - } - ThmExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute."); - - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); - ThmExitOnFailure(hr, "Failed to parse billboard children."); - } - else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon); - ThmExitOnFailure(hr, "Failed while parsing control icon."); - } - else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) - { - hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute."); - - if (fValue) - { - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE; - } - } - } - else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type) - { - hr = XmlGetAttributeNumber(pixn, L"HoverFontId", &pControl->dwFontHoverId); - if (S_FALSE == hr) - { - pControl->dwFontHoverId = THEME_INVALID_ID; - } - ThmExitOnFailure(hr, "Failed when querying control HoverFontId attribute."); - - hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId); - if (S_FALSE == hr) - { - pControl->dwFontSelectedId = THEME_INVALID_ID; - } - ThmExitOnFailure(hr, "Failed when querying control SelectedFontId attribute."); - } - else if (THEME_CONTROL_TYPE_LABEL == pControl->type) - { - hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= SS_CENTER; - } - ThmExitOnFailure(hr, "Failed when querying Label/@Center attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= SS_NOPREFIX; - } - ThmExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute."); - } - else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - // Parse the optional extended window style. - hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle); - ThmExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute."); - - hr = XmlGetAttribute(pixn, L"ImageList", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText); - } - - hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText); - } - - hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText); - } - - hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText); - } - - hr = ParseColumns(pixn, pControl); - ThmExitOnFailure(hr, "Failed to parse columns."); - } - else if (THEME_CONTROL_TYPE_PANEL == pControl->type) - { - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); - ThmExitOnFailure(hr, "Failed to parse panel children."); - } - else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type) - { - hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute."); - } - else if (THEME_CONTROL_TYPE_TAB == pControl->type) - { - hr = ParseTabs(pixn, pControl); - ThmExitOnFailure(hr, "Failed to parse tabs"); - } - else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type) - { - pControl->dwStyle |= TVS_DISABLEDRAGDROP; - - hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle &= ~TVS_DISABLEDRAGDROP; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_FULLROWSELECT; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_HASBUTTONS; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_SHOWSELALWAYS; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_LINESATROOT; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_HASLINES; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute."); - } - -LExit: - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseActions( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrType = NULL; - - hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl); - ThmExitOnFailure(hr, "Failed to select child action nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cActions)); - ThmExitOnFailure(hr, "Failed to count the number of action nodes."); - - if (0 < pControl->cActions) - { - MemAllocArray(reinterpret_cast(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions); - ThmExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType))) - { - if (!bstrType) - { - hr = E_UNEXPECTED; - ThmExitOnFailure(hr, "Null element encountered!"); - } - - THEME_ACTION* pAction = pControl->rgActions + i; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1)) - { - pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY; - - hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName); - ThmExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1)) - { - pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE; - - hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName); - ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute."); - - hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel); - if (E_NOTFOUND != hr) - { - ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute."); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1)) - { - pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW; - } - else - { - hr = E_UNEXPECTED; - ThmExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType); - } - - hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition); - if (E_NOTFOUND != hr) - { - ThmExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType); - } - - if (!pAction->sczCondition) - { - if (pControl->pDefaultAction) - { - hr = E_INVALIDDATA; - ThmExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName); - } - - pControl->pDefaultAction = pAction; - } - - ++i; - ReleaseNullBSTR(bstrType); - ReleaseObject(pixnChild); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrType); - - return hr; -} - - -static HRESULT ParseColumns( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - DWORD dwValue = 0; - - hr = XmlSelectNodes(pixn, L"Column", &pixnl); - ThmExitOnFailure(hr, "Failed to select child column nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cColumns)); - ThmExitOnFailure(hr, "Failed to count the number of control columns."); - - if (0 < pControl->cColumns) - { - hr = MemAllocArray(reinterpret_cast(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns); - ThmExitOnFailure(hr, "Failed to allocate column structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - THEME_COLUMN* pColumn = pControl->ptcColumns + i; - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of column element."); - - hr = XmlGetAttributeNumber(pixnChild, L"Width", &dwValue); - if (S_FALSE == hr) - { - dwValue = 100; - } - ThmExitOnFailure(hr, "Failed to get column width attribute."); - - pColumn->nBaseWidth = pColumn->nDefaultDpiBaseWidth = dwValue; - - hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pColumn->fExpands)); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get expands attribute."); - - hr = StrAllocString(&pColumn->pszName, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy column name."); - - ++i; - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseRadioButtons( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in THEME_PAGE* pPage - ) -{ - HRESULT hr = S_OK; - DWORD cRadioButtons = 0; - DWORD iControl = 0; - DWORD iPageControl = 0; - IXMLDOMNodeList* pixnlRadioButtons = NULL; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnRadioButtons = NULL; - IXMLDOMNode* pixnChild = NULL; - LPWSTR sczName = NULL; - THEME_CONTROL* pControl = NULL; - BOOL fFirst = FALSE; - DWORD* pcControls = NULL; - THEME_CONTROL** prgControls = NULL; - - GetControls(pTheme, pParentControl, &pcControls, &prgControls); - - hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons); - ThmExitOnFailure(hr, "Failed to select RadioButtons nodes."); - - while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL))) - { - hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying RadioButtons Name."); - - hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl); - ThmExitOnFailure(hr, "Failed to select RadioButton nodes."); - - hr = pixnl->get_length(reinterpret_cast(&cRadioButtons)); - ThmExitOnFailure(hr, "Failed to count the number of RadioButton nodes."); - - if (cRadioButtons) - { - if (pPage) - { - iPageControl = pPage->cControlIndices; - pPage->cControlIndices += cRadioButtons; - } - - hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons); - ThmExitOnFailure(hr, "Failed to reallocate theme controls."); - - iControl = *pcControls; - *pcControls += cRadioButtons; - - fFirst = TRUE; - - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - pControl = *prgControls + iControl; - pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON; - - hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage); - ThmExitOnFailure(hr, "Failed to parse control."); - - if (fFirst) - { - pControl->dwStyle |= WS_GROUP; - fFirst = FALSE; - } - - hr = StrAllocString(&pControl->sczVariable, sczName, 0); - ThmExitOnFailure(hr, "Failed to copy radio button variable."); - - if (pPage) - { - pControl->wPageId = pPage->wId; - ++iPageControl; - } - - ++iControl; - } - - if (!fFirst) - { - pControl->fLastRadioButton = TRUE; - } - } - } - -LExit: - ReleaseStr(sczName); - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseObject(pixnlRadioButtons); - ReleaseObject(pixnRadioButtons); - - return hr; -} - - -static HRESULT ParseTabs( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectNodes(pixn, L"Tab", &pixnl); - ThmExitOnFailure(hr, "Failed to select child tab nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cTabs)); - ThmExitOnFailure(hr, "Failed to count the number of tabs."); - - if (0 < pControl->cTabs) - { - hr = MemAllocArray(reinterpret_cast(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs); - ThmExitOnFailure(hr, "Failed to allocate tab structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of tab element."); - - hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy tab name."); - - ++i; - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseText( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectNodes(pixn, L"Text", &pixnl); - ThmExitOnFailure(hr, "Failed to select child Text nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalText)); - ThmExitOnFailure(hr, "Failed to count the number of Text nodes."); - - *pfAnyChildren |= 0 < pControl->cConditionalText; - - if (0 < pControl->cConditionalText) - { - MemAllocArray(reinterpret_cast(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText); - ThmExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i; - - hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying Text/@Condition attribute."); - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of Text element."); - - if (S_OK == hr) - { - if (pConditionalText->sczCondition) - { - hr = StrAllocString(&pConditionalText->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to conditional text."); - - ++i; - } - else - { - if (pControl->sczText) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName); - } - - hr = StrAllocString(&pControl->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to control."); - - // Unconditional text entries aren't stored in the conditional text list. - --pControl->cConditionalText; - } - } - - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseTooltips( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren -) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild); - ThmExitOnFailure(hr, "Failed to select child Tooltip node."); - - if (S_OK == hr) - { - *pfAnyChildren |= TRUE; - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of Tooltip element."); - - if (S_OK == hr) - { - hr = StrAllocString(&pControl->sczTooltip, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy tooltip text to control."); - } - } - -LExit: - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseNotes( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __out BOOL* pfAnyChildren - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectNodes(pixn, L"Note", &pixnl); - ThmExitOnFailure(hr, "Failed to select child Note nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalNotes)); - ThmExitOnFailure(hr, "Failed to count the number of Note nodes."); - - if (pfAnyChildren) - { - *pfAnyChildren = 0 < pControl->cConditionalNotes; - } - - if (0 < pControl->cConditionalNotes) - { - MemAllocArray(reinterpret_cast(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes); - ThmExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + i; - - hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying Note/@Condition attribute."); - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of Note element."); - - if (S_OK == hr) - { - if (pConditionalNote->sczCondition) - { - hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to conditional note text."); - - ++i; - } - else - { - if (pControl->sczNote) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName); - } - - hr = StrAllocString(&pControl->sczNote, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to command link control."); - - // Unconditional note entries aren't stored in the conditional notes list. - --pControl->cConditionalNotes; - } - } - - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT StartBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HRESULT hr = E_NOTFOUND; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - // kick off - pControl->dwData = 0; - OnBillboardTimer(pTheme, pTheme->hwndParent, dwControl); - - if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL)) - { - ThmExitWithLastError(hr, "Failed to start billboard."); - } - - hr = S_OK; - } - } - -LExit: - return hr; -} - - -static HRESULT StopBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HRESULT hr = E_NOTFOUND; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - ThemeControlEnable(pTheme, dwControl, FALSE); - - if (::KillTimer(pTheme->hwndParent, pControl->wId)) - { - hr = S_OK; - } - } - } - - return hr; -} - -static HRESULT EnsureFontInstance( - __in THEME* pTheme, - __in THEME_FONT* pFont, - __out THEME_FONT_INSTANCE** ppFontInstance - ) -{ - HRESULT hr = S_OK; - THEME_FONT_INSTANCE* pFontInstance = NULL; - LOGFONTW lf = { }; - - for (DWORD i = 0; i < pFont->cFontInstances; ++i) - { - pFontInstance = pFont->rgFontInstances + i; - if (pTheme->nDpi == pFontInstance->nDpi) - { - *ppFontInstance = pFontInstance; - ExitFunction(); - } - } - - hr = MemEnsureArraySize(reinterpret_cast(&pFont->rgFontInstances), pFont->cFontInstances, sizeof(THEME_FONT_INSTANCE), GROW_FONT_INSTANCES); - ThmExitOnFailure(hr, "Failed to allocate memory for font instances."); - - pFontInstance = pFont->rgFontInstances + pFont->cFontInstances; - pFontInstance->nDpi = pTheme->nDpi; - - lf.lfHeight = DpiuScaleValue(pFont->lfHeight, pFontInstance->nDpi); - lf.lfWeight = pFont->lfWeight; - lf.lfUnderline = pFont->lfUnderline; - lf.lfQuality = pFont->lfQuality; - - hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), pFont->sczFaceName); - ThmExitOnFailure(hr, "Failed to copy font name to create font."); - - pFontInstance->hFont = ::CreateFontIndirectW(&lf); - ThmExitOnNull(pFontInstance->hFont, hr, E_OUTOFMEMORY, "Failed to create DPI specific font."); - - ++pFont->cFontInstances; - *ppFontInstance = pFontInstance; - -LExit: - return hr; -} - - -static HRESULT FindImageList( - __in THEME* pTheme, - __in_z LPCWSTR wzImageListName, - __out HIMAGELIST *phImageList - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pTheme->cImageLists; ++i) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1)) - { - *phImageList = pTheme->rgImageLists[i].hImageList; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - - -static HRESULT DrawButton( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; - int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; - DWORD dwSourceWidth = pControl->nDefaultDpiWidth; - DWORD dwSourceHeight = pControl->nDefaultDpiHeight; - - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); - - DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE); - // "clicked" gets priority - if (ODS_SELECTED & pdis->itemState) - { - nSourceY += pControl->nDefaultDpiHeight * 2; - } - // then hover - else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) - { - nSourceY += pControl->nDefaultDpiHeight; - } - // then focused - else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState) - { - nSourceY += pControl->nDefaultDpiHeight * 3; - } - - ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - - DrawControlText(pTheme, pdis, pControl, TRUE, FALSE); - - return S_OK; -} - - -static HRESULT DrawHyperlink( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - DrawControlText(pTheme, pdis, pControl, FALSE, TRUE); - return S_OK; -} - - -static void DrawControlText( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl, - __in BOOL fCentered, - __in BOOL fDrawFocusRect - ) -{ - HRESULT hr = S_OK; - WCHAR wzText[256] = { }; - DWORD cchText = 0; - THEME_FONT* pFont = NULL; - THEME_FONT_INSTANCE* pFontInstance = NULL; - HFONT hfPrev = NULL; - - if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText)))) - { - // nothing to do - return; - } - - if (ODS_SELECTED & pdis->itemState) - { - pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId); - } - else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) - { - pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId); - } - else - { - pFont = pTheme->rgFonts + pControl->dwFontId; - } - - hr = EnsureFontInstance(pTheme, pFont, &pFontInstance); - if (SUCCEEDED(hr)) - { - hfPrev = SelectFont(pdis->hDC, pFontInstance->hFont); - } - - ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL); - - if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState)) - { - ::DrawFocusRect(pdis->hDC, &pdis->rcItem); - } - - if (hfPrev) - { - SelectFont(pdis->hDC, hfPrev); - } -} - - -static HRESULT DrawImage( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; - DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left; - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; - int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; - DWORD dwSourceHeight = pControl->nDefaultDpiHeight; - DWORD dwSourceWidth = pControl->nDefaultDpiWidth; - - BLENDFUNCTION bf = { }; - bf.BlendOp = AC_SRC_OVER; - bf.SourceConstantAlpha = 255; - bf.AlphaFormat = AC_SRC_ALPHA; - - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); - - // Try to draw the image with transparency and if that fails (usually because the image has no - // alpha channel) then draw the image as is. - if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, bf)) - { - ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); - } - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - return S_OK; -} - - -static HRESULT DrawProgressBar( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - DWORD dwProgressColor = HIWORD(pControl->dwData); - DWORD dwProgressPercentage = LOWORD(pControl->dwData); - DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; - DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100; - DWORD dwSourceHeight = pControl->nDefaultDpiHeight; - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; - int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * dwSourceHeight); - - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); - - // Draw the left side of the progress bar. - ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY); - - // Draw the filled side of the progress bar, if there is any. - if (0 < dwCenter) - { - ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwSourceHeight, SRCCOPY); - } - - // Draw the unfilled side of the progress bar, if there is any. - if (dwCenter < static_cast(pdis->rcItem.right - 2)) - { - ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwSourceHeight, SRCCOPY); - } - - // Draw the right side of the progress bar. - ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX + 3, nSourceY, 1, dwSourceHeight, SRCCOPY); - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - return S_OK; -} - - -static BOOL DrawHoverControl( - __in THEME* pTheme, - __in BOOL fHover - ) -{ - BOOL fChangedHover = FALSE; - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, pTheme->hwndHover)); - - // Only hyperlinks and owner-drawn buttons have hover states. - if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || - (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)))) - { - if (fHover) - { - pControl->dwData |= THEME_CONTROL_DATA_HOVER; - } - else - { - pControl->dwData &= ~THEME_CONTROL_DATA_HOVER; - } - - ::InvalidateRect(pControl->hWnd, NULL, FALSE); - fChangedHover = TRUE; - } - - return fChangedHover; -} - - -static void FreePage( - __in THEME_PAGE* pPage - ) -{ - if (pPage) - { - ReleaseStr(pPage->sczName); - - if (pPage->cSavedVariables) - { - for (DWORD i = 0; i < pPage->cSavedVariables; ++i) - { - ReleaseStr(pPage->rgSavedVariables[i].sczValue); - } - } - - ReleaseMem(pPage->rgSavedVariables); - } -} - - -static void FreeImageList( - __in THEME_IMAGELIST* pImageList - ) -{ - if (pImageList) - { - ReleaseStr(pImageList->sczName); - ImageList_Destroy(pImageList->hImageList); - } -} - -static void FreeControl( - __in THEME_CONTROL* pControl - ) -{ - if (pControl) - { - if (::IsWindow(pControl->hWnd)) - { - ::CloseWindow(pControl->hWnd); - pControl->hWnd = NULL; - } - - ReleaseStr(pControl->sczName); - ReleaseStr(pControl->sczText); - ReleaseStr(pControl->sczTooltip); - ReleaseStr(pControl->sczNote); - ReleaseStr(pControl->sczEnableCondition); - ReleaseStr(pControl->sczVisibleCondition); - ReleaseStr(pControl->sczValue); - ReleaseStr(pControl->sczVariable); - - if (pControl->hImage) - { - ::DeleteBitmap(pControl->hImage); - } - - for (DWORD i = 0; i < pControl->cControls; ++i) - { - FreeControl(pControl->rgControls + i); - } - - for (DWORD i = 0; i < pControl->cActions; ++i) - { - FreeAction(&(pControl->rgActions[i])); - } - - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - FreeColumn(&(pControl->ptcColumns[i])); - } - - for (DWORD i = 0; i < pControl->cConditionalText; ++i) - { - FreeConditionalText(&(pControl->rgConditionalText[i])); - } - - for (DWORD i = 0; i < pControl->cConditionalNotes; ++i) - { - FreeConditionalText(&(pControl->rgConditionalNotes[i])); - } - - for (DWORD i = 0; i < pControl->cTabs; ++i) - { - FreeTab(&(pControl->pttTabs[i])); - } - - ReleaseMem(pControl->rgActions) - ReleaseMem(pControl->ptcColumns); - ReleaseMem(pControl->rgConditionalText); - ReleaseMem(pControl->rgConditionalNotes); - ReleaseMem(pControl->pttTabs); - } -} - - -static void FreeAction( - __in THEME_ACTION* pAction - ) -{ - switch (pAction->type) - { - case THEME_ACTION_TYPE_BROWSE_DIRECTORY: - ReleaseStr(pAction->BrowseDirectory.sczVariableName); - break; - case THEME_ACTION_TYPE_CHANGE_PAGE: - ReleaseStr(pAction->ChangePage.sczPageName); - break; - } - - ReleaseStr(pAction->sczCondition); -} - - -static void FreeColumn( - __in THEME_COLUMN* pColumn - ) -{ - ReleaseStr(pColumn->pszName); -} - - -static void FreeConditionalText( - __in THEME_CONDITIONAL_TEXT* pConditionalText - ) -{ - ReleaseStr(pConditionalText->sczCondition); - ReleaseStr(pConditionalText->sczText); -} - - -static void FreeTab( - __in THEME_TAB* pTab - ) -{ - ReleaseStr(pTab->pszName); -} - - -static void FreeFontInstance( - __in THEME_FONT_INSTANCE* pFontInstance - ) -{ - if (pFontInstance->hFont) - { - ::DeleteObject(pFontInstance->hFont); - pFontInstance->hFont = NULL; - } -} - - -static void FreeFont( - __in THEME_FONT* pFont - ) -{ - if (pFont) - { - if (pFont->hBackground) - { - ::DeleteObject(pFont->hBackground); - pFont->hBackground = NULL; - } - - if (pFont->hForeground) - { - ::DeleteObject(pFont->hForeground); - pFont->hForeground = NULL; - } - - for (DWORD i = 0; i < pFont->cFontInstances; ++i) - { - FreeFontInstance(&(pFont->rgFontInstances[i])); - } - - ReleaseMem(pFont->rgFontInstances); - ReleaseStr(pFont->sczFaceName); - } -} - - -static DWORD CALLBACK RichEditStreamFromFileHandleCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG* pcb - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = reinterpret_cast(dwCookie); - - if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast(pcb), NULL)) - { - ThmExitWithLastError(hr, "Failed to read file"); - } - -LExit: - return hr; -} - - -static DWORD CALLBACK RichEditStreamFromMemoryCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG* pcb - ) -{ - HRESULT hr = S_OK; - MEMBUFFER_FOR_RICHEDIT* pBuffer = reinterpret_cast(dwCookie); - DWORD cbCopy = 0; - - if (pBuffer->iData < pBuffer->cbData) - { - cbCopy = min(static_cast(cb), pBuffer->cbData - pBuffer->iData); - memcpy(pbBuff, pBuffer->rgbData + pBuffer->iData, cbCopy); - - pBuffer->iData += cbCopy; - Assert(pBuffer->iData <= pBuffer->cbData); - } - - *pcb = cbCopy; - return hr; -} - - -static void CALLBACK OnBillboardTimer( - __in THEME* pTheme, - __in HWND hwnd, - __in UINT_PTR idEvent - ) -{ - HWND hwndControl = ::GetDlgItem(hwnd, static_cast(idEvent)); - if (hwndControl) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hwndControl)); - AssertSz(pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages."); - - if (pControl) - { - if (pControl->dwData < pControl->cControls) - { - ThemeShowChild(pTheme, pControl, pControl->dwData); - } - else if (pControl->fBillboardLoops) - { - pControl->dwData = 0; - ThemeShowChild(pTheme, pControl, pControl->dwData); - } - else // no more looping - { - ::KillTimer(hwnd, idEvent); - } - - ++pControl->dwData; - } - } -} - -static void OnBrowseDirectory( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - WCHAR wzPath[MAX_PATH] = { }; - BROWSEINFOW browseInfo = { }; - PIDLIST_ABSOLUTE pidl = NULL; - - browseInfo.hwndOwner = hWnd; - browseInfo.pszDisplayName = wzPath; - browseInfo.lpszTitle = pTheme->sczCaption; - browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; - pidl = ::SHBrowseForFolderW(&browseInfo); - if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) - { - // Since editbox changes aren't immediately saved off, we have to treat them differently. - THEME_CONTROL* pTargetControl = NULL; - - for (DWORD i = 0; i < pTheme->cControls; ++i) - { - THEME_CONTROL* pControl = pTheme->rgControls + i; - - if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) && - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1)) - { - pTargetControl = pControl; - break; - } - } - - if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality) - { - hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); - ThmExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName); - } - else if (pTheme->pfnSetStringVariable) - { - hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, FALSE, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); - } - else if (pTargetControl) - { - hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); - ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName); - } - - ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); - } - -LExit: - if (pidl) - { - ::CoTaskMemFree(pidl); - } -} - -static BOOL OnButtonClicked( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - BOOL fHandled = FALSE; - - if (THEME_CONTROL_TYPE_BUTTON == pControl->type || THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - if (pControl->cActions) - { - fHandled = TRUE; - THEME_ACTION* pChosenAction = pControl->pDefaultAction; - - if (pTheme->pfnEvaluateCondition) - { - // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. - // This is the current implementation and can change at any time. - for (DWORD j = 0; j < pControl->cActions; ++j) - { - THEME_ACTION* pAction = pControl->rgActions + j; - - if (pAction->sczCondition) - { - BOOL fCondition = FALSE; - - hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition); - - if (fCondition) - { - pChosenAction = pAction; - break; - } - } - } - } - - if (pChosenAction) - { - switch (pChosenAction->type) - { - case THEME_ACTION_TYPE_BROWSE_DIRECTORY: - OnBrowseDirectory(pTheme, hWnd, pChosenAction); - break; - - case THEME_ACTION_TYPE_CLOSE_WINDOW: - ::SendMessageW(hWnd, WM_CLOSE, 0, 0); - break; - - case THEME_ACTION_TYPE_CHANGE_PAGE: - DWORD dwPageId = 0; - LPCWSTR pPageNames = pChosenAction->ChangePage.sczPageName; - ThemeGetPageIds(pTheme, &pPageNames, &dwPageId, 1); - - if (!dwPageId) - { - ThmExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName); - } - - ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT); - ThemeShowPage(pTheme, dwPageId, SW_SHOW); - break; - } - } - } - } - else if (!pControl->fDisableVariableFunctionality && (pTheme->pfnSetNumericVariable || pTheme->pfnSetStringVariable)) - { - BOOL fRefresh = FALSE; - - switch (pControl->type) - { - case THEME_CONTROL_TYPE_CHECKBOX: - if (pTheme->pfnSetNumericVariable && pControl->sczName && *pControl->sczName) - { - BOOL fChecked = ThemeIsControlChecked(pTheme, pControl->wId); - pTheme->pfnSetNumericVariable(pControl->sczName, fChecked ? 1 : 0, pTheme->pvVariableContext); - fRefresh = TRUE; - } - break; - case THEME_CONTROL_TYPE_RADIOBUTTON: - if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId)) - { - pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, FALSE, pTheme->pvVariableContext); - fRefresh = TRUE; - } - break; - } - - if (fRefresh) - { - ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); - fHandled = TRUE; - } - } - -LExit: - return fHandled; -} - -static BOOL OnDpiChanged( - __in THEME* pTheme, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - UINT nDpi = HIWORD(wParam); - RECT* pRect = reinterpret_cast(lParam); - BOOL fIgnored = pTheme->nDpi == nDpi; - - if (fIgnored) - { - ExitFunction(); - } - - - pTheme->fForceResize = !pTheme->fAutoResize; - ScaleThemeFromWindow(pTheme, nDpi, pRect->left, pRect->top); - -LExit: - return !fIgnored; -} - -static void OnNcCreate( - __in THEME* pTheme, - __in HWND hWnd, - __in LPARAM lParam - ) -{ - DPIU_WINDOW_CONTEXT windowContext = { }; - CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); - - pTheme->hwndParent = hWnd; - - DpiuGetWindowContext(pTheme->hwndParent, &windowContext); - - if (windowContext.nDpi != pTheme->nDpi) - { - ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y, pCreateStruct->style, NULL != pCreateStruct->hMenu, pCreateStruct->dwExStyle); - } -} - -static HRESULT OnRichEditEnLink( - __in LPARAM lParam, - __in HWND hWndRichEdit, - __in HWND hWnd - ) -{ - HRESULT hr = S_OK; - LPWSTR sczLink = NULL; - ENLINK* link = reinterpret_cast(lParam); - - switch (link->msg) - { - case WM_LBUTTONDOWN: - { - hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2); - ThmExitOnFailure(hr, "Failed to allocate string for link."); - - TEXTRANGEW tr; - tr.chrg.cpMin = link->chrg.cpMin; - tr.chrg.cpMax = link->chrg.cpMax; - tr.lpstrText = sczLink; - - if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast(&tr))) - { - hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); - ThmExitOnFailure(hr, "Failed to launch link: %ls", sczLink); - } - - break; - } - - case WM_SETCURSOR: - ::SetCursor(vhCursorHand); - break; - } - -LExit: - ReleaseStr(sczLink); - - return hr; -} - -static BOOL ControlIsType( - __in const THEME* pTheme, - __in DWORD dwControl, - __in const THEME_CONTROL_TYPE type - ) -{ - BOOL fIsType = FALSE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hWnd) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - fIsType = (pControl && type == pControl->type); - } - - return fIsType; -} - -static const THEME_CONTROL* FindControlFromHWnd( - __in const THEME* pTheme, - __in HWND hWnd, - __in_opt const THEME_CONTROL* pParentControl - ) -{ - DWORD cControls = 0; - THEME_CONTROL* rgControls = NULL; - - GetControls(pTheme, pParentControl, cControls, rgControls); - - // As we can't use GWLP_USERDATA (SysLink controls on Windows XP uses it too)... - for (DWORD i = 0; i < cControls; ++i) - { - if (hWnd == rgControls[i].hWnd) - { - return rgControls + i; - } - else if (0 < rgControls[i].cControls) - { - const THEME_CONTROL* pChildControl = FindControlFromHWnd(pTheme, hWnd, rgControls + i); - if (pChildControl) - { - return pChildControl; - } - } - } - - return NULL; -} - -static void GetControlDimensions( - __in const THEME_CONTROL* pControl, - __in const RECT* prcParent, - __out int* piWidth, - __out int* piHeight, - __out int* piX, - __out int* piY - ) -{ - *piWidth = pControl->nWidth + (0 < pControl->nWidth ? 0 : prcParent->right - max(0, pControl->nX)); - *piHeight = pControl->nHeight + (0 < pControl->nHeight ? 0 : prcParent->bottom - max(0, pControl->nY)); - *piX = pControl->nX + (-1 < pControl->nX ? 0 : prcParent->right - *piWidth); - *piY = pControl->nY + (-1 < pControl->nY ? 0 : prcParent->bottom - *piHeight); -} - -static HRESULT SizeListViewColumns( - __inout THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - RECT rcParent = { }; - int cNumExpandingColumns = 0; - int iExtraAvailableSize; - - if (!::GetWindowRect(pControl->hWnd, &rcParent)) - { - ThmExitWithLastError(hr, "Failed to get window rect of listview control."); - } - - iExtraAvailableSize = rcParent.right - rcParent.left; - - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - if (pControl->ptcColumns[i].fExpands) - { - ++cNumExpandingColumns; - } - - iExtraAvailableSize -= pControl->ptcColumns[i].nBaseWidth; - } - - // Leave room for a vertical scroll bar just in case. - iExtraAvailableSize -= ::GetSystemMetrics(SM_CXVSCROLL); - - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - if (pControl->ptcColumns[i].fExpands) - { - pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth + (iExtraAvailableSize / cNumExpandingColumns); - // In case there is any remainder, use it up the first chance we get. - pControl->ptcColumns[i].nWidth += iExtraAvailableSize % cNumExpandingColumns; - iExtraAvailableSize -= iExtraAvailableSize % cNumExpandingColumns; - } - else - { - pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth; - } - } - -LExit: - return hr; -} - - -static HRESULT ShowControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId, - __out_opt HWND* phwndFocus - ) -{ - HRESULT hr = S_OK; - DWORD iPageControl = 0; - HWND hwndFocus = NULL; - LPWSTR sczFormatString = NULL; - LPWSTR sczText = NULL; - THEME_SAVEDVARIABLE* pSavedVariable = NULL; - BOOL fHide = SW_HIDE == nCmdShow; - THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPageId); - - // Save the editbox value if necessary (other control types save their values immediately). - if (pTheme->pfnSetStringVariable && !pControl->fDisableVariableFunctionality && - fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) - { - hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText); - ThmExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); - - hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, FALSE, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); - } - - HWND hWnd = pControl->hWnd; - - if (fHide && pControl->wPageId) - { - ::ShowWindow(hWnd, SW_HIDE); - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - StopBillboard(pTheme, pControl->wId); - } - - ExitFunction(); - } - - BOOL fEnabled = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); - BOOL fVisible = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDDEN); - - if (!pControl->fDisableVariableFunctionality) - { - if (pTheme->pfnEvaluateCondition) - { - // If the control has a VisibleCondition, check if it's true. - if (pControl->sczVisibleCondition) - { - hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); - } - - // If the control has an EnableCondition, check if it's true. - if (pControl->sczEnableCondition) - { - hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); - } - } - - // Try to format each control's text based on context, except for editboxes since their text comes from the user. - if (pTheme->pfnFormatString && ((pControl->sczText && *pControl->sczText) || pControl->cConditionalText) && THEME_CONTROL_TYPE_EDITBOX != pControl->type) - { - LPWSTR wzText = pControl->sczText; - LPWSTR wzNote = pControl->sczNote; - - if (pTheme->pfnEvaluateCondition) - { - // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. - // This is the current implementation and can change at any time. - for (DWORD j = 0; j < pControl->cConditionalText; ++j) - { - THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + j; - wzText = pConditionalText->sczText; - - if (pConditionalText->sczCondition) - { - BOOL fCondition = FALSE; - - hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition); - - if (fCondition) - { - wzText = pConditionalText->sczText; - break; - } - } - } - - for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) - { - THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + j; - wzNote = pConditionalNote->sczText; - - if (pConditionalNote->sczCondition) - { - BOOL fCondition = FALSE; - - hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition); - - if (fCondition) - { - wzNote = pConditionalNote->sczText; - break; - } - } - } - } - - if (wzText && *wzText) - { - hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to format string: %ls", wzText); - } - else - { - ReleaseNullStr(sczText); - } - - ThemeSetTextControl(pTheme, pControl->wId, sczText); - - if (wzNote && *wzNote) - { - hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to format note: %ls", wzNote); - } - else - { - ReleaseNullStr(sczText); - } - - ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(sczText)); - } - - // If this is a named control, do variable magic. - if (pControl->sczName && *pControl->sczName) - { - // If this is a checkbox control, - // try to set its default state to the state of a matching named variable. - if (pTheme->pfnGetNumericVariable && THEME_CONTROL_TYPE_CHECKBOX == pControl->type) - { - LONGLONG llValue = 0; - hr = pTheme->pfnGetNumericVariable(pControl->sczName, &llValue, pTheme->pvVariableContext); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName); - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) - { - pSavedVariable = pPage->rgSavedVariables + iPageControl; - pSavedVariable->wzName = pControl->sczName; - - if (SUCCEEDED(hr)) - { - hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue); - ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); - } - - ++iPageControl; - } - - ThemeSendControlMessage(pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); - } - - // If this is an editbox control, - // try to set its default state to the state of a matching named variable. - if (pTheme->pfnFormatString && THEME_CONTROL_TYPE_EDITBOX == pControl->type) - { - hr = StrAllocFormatted(&sczFormatString, L"[%ls]", pControl->sczName); - ThmExitOnFailure(hr, "Failed to create format string: '%ls'", pControl->sczName); - - hr = pTheme->pfnFormatString(sczFormatString, &sczText, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to format string: '%ls'", sczFormatString); - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) - { - pSavedVariable = pPage->rgSavedVariables + iPageControl; - pSavedVariable->wzName = pControl->sczName; - - if (SUCCEEDED(hr)) - { - hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); - ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); - } - - ++iPageControl; - } - - ThemeSetTextControl(pTheme, pControl->wId, sczText); - } - } - - // If this is a radio button associated with a variable, - // try to set its default state to the state of the variable. - if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type && pControl->sczVariable && *pControl->sczVariable) - { - hr = pTheme->pfnGetStringVariable(pControl->sczVariable, &sczText, pTheme->pvVariableContext); - if (E_NOTFOUND == hr) - { - ReleaseNullStr(sczText); - } - else - { - ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable); - } - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton) - { - pSavedVariable = pPage->rgSavedVariables + iPageControl; - pSavedVariable->wzName = pControl->sczVariable; - - if (SUCCEEDED(hr)) - { - hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); - ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable); - } - - ++iPageControl; - } - - hr = S_OK; - - Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || (sczText && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1))); - } - } - - if (!fVisible || (!fEnabled && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED))) - { - ::ShowWindow(hWnd, SW_HIDE); - } - else - { - ::EnableWindow(hWnd, !fHide && fEnabled); - - if (!hwndFocus && pControl->wPageId && (pControl->dwStyle & WS_TABSTOP)) - { - hwndFocus = hWnd; - } - - ::ShowWindow(hWnd, nCmdShow); - } - - if (0 < pControl->cControls) - { - ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId); - } - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId) - { - if (fEnabled) - { - StartBillboard(pTheme, pControl->wId); - } - else - { - StopBillboard(pTheme, pControl->wId); - } - } - - if (phwndFocus) - { - *phwndFocus = hwndFocus; - } - -LExit: - ReleaseStr(sczFormatString); - ReleaseStr(sczText); - - return hr; -} - -static HRESULT ShowControls( - __in THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId - ) -{ - HRESULT hr = S_OK; - HWND hwndFocus = NULL; - DWORD cControls = 0; - THEME_CONTROL* rgControls = NULL; - - GetControls(pTheme, pParentControl, cControls, rgControls); - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - - // Only look at non-page controls and the specified page's controls. - if (!pControl->wPageId || pControl->wPageId == dwPageId) - { - hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus); - ThmExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i); - } - } - - if (hwndFocus) - { - ::SetFocus(hwndFocus); - } - -LExit: - return hr; -} - - -static LRESULT CALLBACK ControlGroupDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - if (pTheme) - { - switch (uMsg) - { - case WM_DRAWITEM: - ThemeDrawControl(pTheme, reinterpret_cast(lParam)); - return TRUE; - - case WM_CTLCOLORBTN: __fallthrough; - case WM_CTLCOLORSTATIC: - { - HBRUSH hBrush = NULL; - if (ThemeSetControlColor(pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam), &hBrush)) - { - return reinterpret_cast(hBrush); - } - } - break; - - case WM_SETCURSOR: - if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast(wParam))) - { - return TRUE; - } - break; - - case WM_PAINT: - if (::GetUpdateRect(hWnd, NULL, FALSE)) - { - PAINTSTRUCT ps; - ::BeginPaint(hWnd, &ps); - if (hWnd == pTheme->hwndParent) - { - ThemeDrawBackground(pTheme, &ps); - } - ::EndPaint(hWnd, &ps); - } - return 0; - - case WM_TIMER: - OnBillboardTimer(pTheme, hWnd, wParam); - break; - - case WM_NOTIFY: - if (lParam) - { - LPNMHDR pnmhdr = reinterpret_cast(lParam); - switch (pnmhdr->code) - { - // Tab/Shift+Tab support for rich-edit control. - case EN_MSGFILTER: - { - MSGFILTER* msgFilter = reinterpret_cast(lParam); - if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam) - { - BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT); - HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift); - ::SetFocus(hwndFocus); - return 1; - } - break; - } - - // Hyperlink clicks from rich-edit control. - case EN_LINK: - return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd)); - - // Clicks on a hypertext/syslink control. - case NM_CLICK: __fallthrough; - case NM_RETURN: - if (ControlIsType(pTheme, static_cast(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT)) - { - PNMLINK pnmlink = reinterpret_cast(lParam); - LITEM litem = pnmlink->item; - ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); - return 1; - } - - return 0; - } - } - break; - - case WM_COMMAND: - switch (HIWORD(wParam)) - { - case BN_CLICKED: - if (lParam) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam); - if (pControl && OnButtonClicked(pTheme, hWnd, pControl)) - { - return 0; - } - } - break; - } - break; - } - } - - return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - - -static LRESULT CALLBACK PanelWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - LRESULT lres = 0; - THEME* pTheme = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); - - switch (uMsg) - { - case WM_NCCREATE: - { - LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); - pTheme = reinterpret_cast(lpcs->lpCreateParams); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pTheme)); - } - break; - - case WM_NCDESTROY: - lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - return lres; - - case WM_NCHITTEST: - return HTCLIENT; - break; - } - - return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); -} - -static LRESULT CALLBACK StaticOwnerDrawWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_UPDATEUISTATE: - return ::DefWindowProc(hWnd, uMsg, wParam, lParam); - default: - return (*vpfnStaticOwnerDrawBaseWndProc)(hWnd, uMsg, wParam, lParam); - } -} - -static HRESULT LoadControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ) -{ - HRESULT hr = S_OK; - RECT rcParent = { }; - LPWSTR sczText = NULL; - BOOL fStartNewGroup = FALSE; - DWORD cControls = 0; - THEME_CONTROL* rgControls = NULL; - HWND hwndParent = pParentControl ? pParentControl->hWnd : pTheme->hwndParent; - int w = 0; - int h = 0; - int x = 0; - int y = 0; - - GetControls(pTheme, pParentControl, cControls, rgControls); - ::GetClientRect(hwndParent, &rcParent); - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; - THEME_FONT_INSTANCE* pControlFontInstance = NULL; - LPCWSTR wzWindowClass = NULL; - DWORD dwWindowBits = WS_CHILD; - DWORD dwWindowExBits = 0; - - if (fStartNewGroup) - { - dwWindowBits |= WS_GROUP; - fStartNewGroup = FALSE; - } - - switch (pControl->type) - { - case THEME_CONTROL_TYPE_BILLBOARD: - __fallthrough; - case THEME_CONTROL_TYPE_PANEL: - wzWindowClass = THEME_WC_PANEL; - dwWindowBits |= WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - dwWindowExBits |= WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT; -#ifdef DEBUG - StrAllocFormatted(&pControl->sczText, L"Panel '%ls', id: %d", pControl->sczName, pControl->wId); -#endif - break; - - case THEME_CONTROL_TYPE_CHECKBOX: - dwWindowBits |= BS_AUTOCHECKBOX | BS_MULTILINE; // checkboxes are basically buttons with an extra bit tossed in. - __fallthrough; - case THEME_CONTROL_TYPE_BUTTON: - wzWindowClass = WC_BUTTONW; - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) - { - dwWindowBits |= BS_OWNERDRAW; - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; - } - break; - - case THEME_CONTROL_TYPE_COMBOBOX: - wzWindowClass = WC_COMBOBOXW; - dwWindowBits |= CBS_DROPDOWNLIST | CBS_HASSTRINGS; - break; - - case THEME_CONTROL_TYPE_COMMANDLINK: - wzWindowClass = WC_BUTTONW; - dwWindowBits |= BS_COMMANDLINK; - break; - - case THEME_CONTROL_TYPE_EDITBOX: - wzWindowClass = WC_EDITW; - dwWindowBits |= ES_LEFT | ES_AUTOHSCROLL; - dwWindowExBits = WS_EX_CLIENTEDGE; - break; - - case THEME_CONTROL_TYPE_HYPERLINK: // hyperlinks are basically just owner drawn buttons. - wzWindowClass = THEME_WC_HYPERLINK; - dwWindowBits |= BS_OWNERDRAW | BTNS_NOPREFIX; - break; - - case THEME_CONTROL_TYPE_HYPERTEXT: - wzWindowClass = WC_LINK; - dwWindowBits |= LWS_NOPREFIX; - break; - - case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps). - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) - { - wzWindowClass = THEME_WC_STATICOWNERDRAW; - dwWindowBits |= SS_OWNERDRAW; - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Invalid image or image list coordinates."); - } - break; - - case THEME_CONTROL_TYPE_LABEL: - wzWindowClass = WC_STATICW; - break; - - case THEME_CONTROL_TYPE_LISTVIEW: - // If thmutil is handling the image list for this listview, tell Windows not to free it when the control is destroyed. - if (pControl->rghImageList[0] || pControl->rghImageList[1] || pControl->rghImageList[2] || pControl->rghImageList[3]) - { - pControl->dwStyle |= LVS_SHAREIMAGELISTS; - } - wzWindowClass = WC_LISTVIEWW; - break; - - case THEME_CONTROL_TYPE_PROGRESSBAR: - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) - { - wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control. - dwWindowBits |= SS_OWNERDRAW; - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; - } - else - { - wzWindowClass = PROGRESS_CLASSW; - } - break; - - case THEME_CONTROL_TYPE_RADIOBUTTON: - dwWindowBits |= BS_AUTORADIOBUTTON | BS_MULTILINE; - wzWindowClass = WC_BUTTONW; - - if (pControl->fLastRadioButton) - { - fStartNewGroup = TRUE; - } - break; - - case THEME_CONTROL_TYPE_RICHEDIT: - if (!vhModuleMsftEdit && !vhModuleRichEd) - { - hr = LoadSystemLibrary(L"Msftedit.dll", &vhModuleMsftEdit); - if (FAILED(hr)) - { - hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); - ThmExitOnFailure(hr, "Failed to load Rich Edit control library."); - } - } - - wzWindowClass = vhModuleMsftEdit ? MSFTEDIT_CLASS : RICHEDIT_CLASSW; - dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY; - break; - - case THEME_CONTROL_TYPE_STATIC: - wzWindowClass = WC_STATICW; - dwWindowBits |= SS_ETCHEDHORZ; - break; - - case THEME_CONTROL_TYPE_TAB: - wzWindowClass = WC_TABCONTROLW; - break; - - case THEME_CONTROL_TYPE_TREEVIEW: - wzWindowClass = WC_TREEVIEWW; - break; - } - ThmExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type); - - // Default control ids to the theme id and its index in the control array, unless there - // is a specific id to assign to a named control. - WORD wControlId = MAKEWORD(i, pTheme->wId); - for (DWORD iAssignControl = 0; pControl->sczName && iAssignControl < cAssignControlIds; ++iAssignControl) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pControl->sczName, -1, rgAssignControlIds[iAssignControl].wzName, -1)) - { - wControlId = rgAssignControlIds[iAssignControl].wId; - break; - } - } - - pControl->wId = wControlId; - - GetControlDimensions(pControl, &rcParent, &w, &h, &x, &y); - - BOOL fVisible = pControl->dwStyle & WS_VISIBLE; - BOOL fDisabled = pControl->dwStyle & WS_DISABLED; - - // If the control is supposed to be initially visible and it has a VisibleCondition, check if it's true. - if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) - { - hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); - - if (!fVisible) - { - pControl->dwStyle &= ~WS_VISIBLE; - } - } - - // Disable controls that aren't visible so their shortcut keys don't trigger. - if (!fVisible) - { - dwWindowBits |= WS_DISABLED; - fDisabled = TRUE; - } - - // If the control is supposed to be initially enabled and it has an EnableCondition, check if it's true. - if (!fDisabled && pControl->sczEnableCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) - { - BOOL fEnable = TRUE; - - hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); - - fDisabled = !fEnable; - dwWindowBits |= fDisabled ? WS_DISABLED : 0; - } - - // Honor the HideWhenDisabled option. - if ((pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) && fVisible && fDisabled) - { - fVisible = FALSE; - pControl->dwStyle &= ~WS_VISIBLE; - } - - pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pTheme); - ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); - - if (pControl->sczTooltip) - { - if (!pTheme->hwndTooltip) - { - 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); - } - - if (pTheme->hwndTooltip) - { - TOOLINFOW toolinfo = {}; - toolinfo.cbSize = sizeof(toolinfo); - toolinfo.hwnd = hwndParent; - toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; - toolinfo.uId = reinterpret_cast(pControl->hWnd); - toolinfo.lpszText = pControl->sczTooltip; - ::SendMessageW(pTheme->hwndTooltip, TTM_ADDTOOLW, 0, reinterpret_cast(&toolinfo)); - } - } - - if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - if (pControl->sczNote) - { - ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(pControl->sczNote)); - } - - if (pControl->hImage) - { - ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast(pControl->hImage)); - } - else if (pControl->hIcon) - { - ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_ICON, reinterpret_cast(pControl->hIcon)); - } - } - else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) - { - if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE) - { - hr = ::SHAutoComplete(pControl->hWnd, SHACF_FILESYS_ONLY); - } - } - else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle); - - hr = SizeListViewColumns(pControl); - ThmExitOnFailure(hr, "Failed to get size of list view columns."); - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - LVCOLUMNW lvc = { }; - lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; - lvc.cx = pControl->ptcColumns[j].nWidth; - lvc.iSubItem = j; - lvc.pszText = pControl->ptcColumns[j].pszName; - lvc.fmt = LVCFMT_LEFT; - lvc.cchTextMax = 4; - - if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc))) - { - ThmExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j); - } - - // Return value tells us the old image list, we don't care. - if (pControl->rghImageList[0]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_NORMAL), reinterpret_cast(pControl->rghImageList[0])); - } - else if (pControl->rghImageList[1]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_SMALL), reinterpret_cast(pControl->rghImageList[1])); - } - else if (pControl->rghImageList[2]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_STATE), reinterpret_cast(pControl->rghImageList[2])); - } - else if (pControl->rghImageList[3]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_GROUPHEADER), reinterpret_cast(pControl->rghImageList[3])); - } - } - } - else if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) - { - ::SendMessageW(pControl->hWnd, EM_AUTOURLDETECT, static_cast(TRUE), 0); - ::SendMessageW(pControl->hWnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_LINK); - } - else if (THEME_CONTROL_TYPE_TAB == pControl->type) - { - ULONG_PTR hbrBackground = 0; - if (THEME_INVALID_ID != pControl->dwFontId) - { - hbrBackground = reinterpret_cast(pTheme->rgFonts[pControl->dwFontId].hBackground); - } - else - { - hbrBackground = ::GetClassLongPtr(pTheme->hwndParent, GCLP_HBRBACKGROUND); - } - ::SetClassLongPtr(pControl->hWnd, GCLP_HBRBACKGROUND, hbrBackground); - - for (DWORD j = 0; j < pControl->cTabs; ++j) - { - TCITEMW tci = { }; - tci.mask = TCIF_TEXT | TCIF_IMAGE; - tci.iImage = -1; - tci.pszText = pControl->pttTabs[j].pszName; - - if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci))) - { - ThmExitWithLastError(hr, "Failed to insert tab %u into tab control.", j); - } - } - } - - if (pControlFont) - { - hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); - ThmExitOnFailure(hr, "Failed to get DPI specific font."); - - ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFontInstance->hFont, FALSE); - } - - // Initialize the text on all "application" (non-page) controls, best effort only. - if (pTheme->pfnFormatString && !pControl->wPageId && pControl->sczText && *pControl->sczText) - { - HRESULT hrFormat = pTheme->pfnFormatString(pControl->sczText, &sczText, pTheme->pvVariableContext); - if (SUCCEEDED(hrFormat)) - { - ThemeSetTextControl(pTheme, pControl->wId, sczText); - } - } - - if (pControl->cControls) - { - hr = LoadControls(pTheme, pControl, rgAssignControlIds, cAssignControlIds); - ThmExitOnFailure(hr, "Failed to load child controls."); - } - } - -LExit: - ReleaseStr(sczText); - - return hr; -} - -static HRESULT LocalizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const WIX_LOCALIZATION *pWixLoc - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - hr = LocalizeControl(pControl, pWixLoc); - ThmExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName); - } - -LExit: - return hr; -} - -static HRESULT LocalizeControl( - __in THEME_CONTROL* pControl, - __in const WIX_LOCALIZATION *pWixLoc - ) -{ - HRESULT hr = S_OK; - LOC_CONTROL* pLocControl = NULL; - LPWSTR sczLocStringId = NULL; - - if (pControl->sczText && *pControl->sczText) - { - hr = LocLocalizeString(pWixLoc, &pControl->sczText); - ThmExitOnFailure(hr, "Failed to localize control text."); - } - else if (pControl->sczName) - { - LOC_STRING* plocString = NULL; - - hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName); - ThmExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName); - - hr = LocGetString(pWixLoc, sczLocStringId, &plocString); - if (E_NOTFOUND != hr) - { - ThmExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName); - - hr = StrAllocString(&pControl->sczText, plocString->wzText, 0); - ThmExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText); - } - } - - if (pControl->sczTooltip && *pControl->sczTooltip) - { - hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip); - ThmExitOnFailure(hr, "Failed to localize control tooltip text."); - } - - if (pControl->sczNote && *pControl->sczNote) - { - hr = LocLocalizeString(pWixLoc, &pControl->sczNote); - ThmExitOnFailure(hr, "Failed to localize control note text."); - } - - for (DWORD j = 0; j < pControl->cConditionalText; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText); - ThmExitOnFailure(hr, "Failed to localize conditional text."); - } - - for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText); - ThmExitOnFailure(hr, "Failed to localize conditional note."); - } - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName); - ThmExitOnFailure(hr, "Failed to localize column text."); - } - - for (DWORD j = 0; j < pControl->cTabs; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName); - ThmExitOnFailure(hr, "Failed to localize tab text."); - } - - // Localize control's size, location, and text. - if (pControl->sczName) - { - hr = LocGetControl(pWixLoc, pControl->sczName, &pLocControl); - if (E_NOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - ThmExitOnFailure(hr, "Failed to localize control."); - - if (LOC_CONTROL_NOT_SET != pLocControl->nX) - { - pControl->nDefaultDpiX = pLocControl->nX; - } - - if (LOC_CONTROL_NOT_SET != pLocControl->nY) - { - pControl->nDefaultDpiY = pLocControl->nY; - } - - if (LOC_CONTROL_NOT_SET != pLocControl->nWidth) - { - pControl->nDefaultDpiWidth = pLocControl->nWidth; - } - - if (LOC_CONTROL_NOT_SET != pLocControl->nHeight) - { - pControl->nDefaultDpiHeight = pLocControl->nHeight; - } - - if (pLocControl->wzText && *pLocControl->wzText) - { - hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0); - ThmExitOnFailure(hr, "Failed to localize control text."); - } - } - - hr = LocalizeControls(pControl->cControls, pControl->rgControls, pWixLoc); - -LExit: - ReleaseStr(sczLocStringId); - - return hr; -} - -static HRESULT LoadControlsString( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in HMODULE hResModule - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - hr = LoadControlString(pControl, hResModule); - ThmExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName); - } - -LExit: - return hr; -} - -static HRESULT LoadControlString( - __in THEME_CONTROL* pControl, - __in HMODULE hResModule - ) -{ - HRESULT hr = S_OK; - if (UINT_MAX != pControl->uStringId) - { - hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText); - ThmExitOnFailure(hr, "Failed to load control text."); - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - if (UINT_MAX != pControl->ptcColumns[j].uStringId) - { - hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName); - ThmExitOnFailure(hr, "Failed to load column text."); - } - } - - for (DWORD j = 0; j < pControl->cTabs; ++j) - { - if (UINT_MAX != pControl->pttTabs[j].uStringId) - { - hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName); - ThmExitOnFailure(hr, "Failed to load tab text."); - } - } - } - - hr = LoadControlsString(pControl->cControls, pControl->rgControls, hResModule); - -LExit: - return hr; -} - -static void ResizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const RECT* prcParent - ) -{ - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - ResizeControl(pControl, prcParent); - } -} - -static void ResizeControl( - __in THEME_CONTROL* pControl, - __in const RECT* prcParent - ) -{ - int w = 0; - int h = 0; - int x = 0; - int y = 0; - RECT rcControl = { }; - - GetControlDimensions(pControl, prcParent, &w, &h, &x, &y); - ::SetWindowPos(pControl->hWnd, NULL, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER); - -#ifdef DEBUG - if (THEME_CONTROL_TYPE_BUTTON == pControl->type) - { - Trace(REPORT_STANDARD, "Resizing button (%ls/%ls) to (%d,%d)+(%d,%d) for parent (%d,%d)-(%d,%d)", - pControl->sczName, pControl->sczText, x, y, w, h, prcParent->left, prcParent->top, prcParent->right, prcParent->bottom); - } -#endif - - if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - SizeListViewColumns(pControl); - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - if (-1 == ::SendMessageW(pControl->hWnd, LVM_SETCOLUMNWIDTH, (WPARAM) (int) (j), (LPARAM) (pControl->ptcColumns[j].nWidth))) - { - Trace(REPORT_DEBUG, "Failed to resize listview column %u with error %u", j, ::GetLastError()); - return; - } - } - } - - if (pControl->cControls) - { - ::GetClientRect(pControl->hWnd, &rcControl); - ResizeControls(pControl->cControls, pControl->rgControls, &rcControl); - } -} - -static void ScaleThemeFromWindow( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y - ) -{ - DWORD dwStyle = GetWindowStyle(pTheme->hwndParent); - BOOL fMenu = NULL != ::GetMenu(pTheme->hwndParent); - DWORD dwExStyle = GetWindowExStyle(pTheme->hwndParent); - - ScaleTheme(pTheme, nDpi, x, y, dwStyle, fMenu, dwExStyle); -} - -static void ScaleTheme( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle - ) -{ - RECT rect = { }; - - pTheme->nDpi = nDpi; - - pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi); - pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi); - pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); - pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); - - rect.left = x; - rect.top = y; - rect.right = x + pTheme->nWidth; - rect.bottom = y + pTheme->nHeight; - DpiuAdjustWindowRect(&rect, dwStyle, fMenu, dwExStyle, pTheme->nDpi); - pTheme->nWindowWidth = rect.right - rect.left; - pTheme->nWindowHeight = rect.bottom - rect.top; - - ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi); - - if (pTheme->hwndParent) - { - ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, SWP_NOACTIVATE | SWP_NOZORDER); - } -} - -static void ScaleControls( - __in THEME* pTheme, - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in UINT nDpi - ) -{ - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - ScaleControl(pTheme, pControl, nDpi); - } -} - -static void ScaleControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in UINT nDpi - ) -{ - HRESULT hr = S_OK; - THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; - THEME_FONT_INSTANCE* pControlFontInstance = NULL; - - if (pControlFont) - { - hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); - if (SUCCEEDED(hr) && pControl->hWnd) - { - ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM)pControlFontInstance->hFont, FALSE); - } - } - - if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - THEME_COLUMN* pColumn = pControl->ptcColumns + i; - - pColumn->nBaseWidth = DpiuScaleValue(pColumn->nDefaultDpiBaseWidth, nDpi); - } - } - - pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi); - pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi); - pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi); - pControl->nY = DpiuScaleValue(pControl->nDefaultDpiY, nDpi); - - if (pControl->cControls) - { - ScaleControls(pTheme, pControl->cControls, pControl->rgControls, nDpi); - } -} - -static void UnloadControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls - ) -{ - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - pControl->hWnd = NULL; - - UnloadControls(pControl->cControls, pControl->rgControls); - } -} - diff --git a/src/dutil/timeutil.cpp b/src/dutil/timeutil.cpp deleted file mode 100644 index b7953c94..00000000 --- a/src/dutil/timeutil.cpp +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define TimeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) -#define TimeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) -#define TimeExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) -#define TimeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) -#define TimeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_TIMEUTIL, e, x, s, __VA_ARGS__) -#define TimeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_TIMEUTIL, g, x, s, __VA_ARGS__) - -const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; -const 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" }; -enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone }; -enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone }; - -// prototypes -static HRESULT DayFromString( - __in_z LPCWSTR wzDay, - __out WORD* pwDayOfWeek - ); -static HRESULT MonthFromString( - __in_z LPCWSTR wzMonth, - __out WORD* pwMonthOfYear - ); - - -/******************************************************************** - TimeFromString - converts string to FILETIME - -*******************************************************************/ -extern "C" HRESULT DAPI TimeFromString( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ) -{ - Assert(wzTime && pFileTime); - - HRESULT hr = S_OK; - LPWSTR pwzTime = NULL; - - SYSTEMTIME sysTime = { }; - TIME_PARSER timeParser = DayOfWeek; - - LPCWSTR pwzStart = NULL; - LPWSTR pwzEnd = NULL; - - hr = StrAllocString(&pwzTime, wzTime, 0); - TimeExitOnFailure(hr, "Failed to copy time."); - - pwzStart = pwzEnd = pwzTime; - while (pwzEnd && *pwzEnd) - { - if (L',' == *pwzEnd || L' ' == *pwzEnd || L':' == *pwzEnd) - { - *pwzEnd = L'\0'; // null terminate - ++pwzEnd; - - while (L' ' == *pwzEnd) - { - ++pwzEnd; // and skip past the blank space - } - - switch (timeParser) - { - case DayOfWeek: - hr = DayFromString(pwzStart, &sysTime.wDayOfWeek); - TimeExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart); - break; - - case DayOfMonth: - sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case MonthOfYear: - hr = MonthFromString(pwzStart, &sysTime.wMonth); - TimeExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart); - break; - - case Year: - sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case Hours: - sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case Minutes: - sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case Seconds: - sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case TimeZone: - // TODO: do something with this in the future, but this should only hit outside of the while loop. - break; - - default: - break; - } - - pwzStart = pwzEnd; - timeParser = (TIME_PARSER)((int)timeParser + 1); - } - - ++pwzEnd; - } - - - if (!::SystemTimeToFileTime(&sysTime, pFileTime)) - { - TimeExitWithLastError(hr, "Failed to convert system time to file time."); - } - -LExit: - ReleaseStr(pwzTime); - - return hr; -} - -/******************************************************************** - TimeFromString3339 - converts string formated in accorance with RFC3339 to FILETIME - http://tools.ietf.org/html/rfc3339 -*******************************************************************/ -extern "C" HRESULT DAPI TimeFromString3339( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ) -{ - Assert(wzTime && pFileTime); - - HRESULT hr = S_OK; - LPWSTR pwzTime = NULL; - - SYSTEMTIME sysTime = { }; - TIME_PARSERRFC3339 timeParser = RFC3339_Year; - - LPCWSTR pwzStart = NULL; - LPWSTR pwzEnd = NULL; - - hr = StrAllocString(&pwzTime, wzTime, 0); - TimeExitOnFailure(hr, "Failed to copy time."); - - pwzStart = pwzEnd = pwzTime; - while (pwzEnd && *pwzEnd) - { - if (L'T' == *pwzEnd || L':' == *pwzEnd || L'-' == *pwzEnd) - { - *pwzEnd = L'\0'; // null terminate - ++pwzEnd; - - switch (timeParser) - { - case RFC3339_Year: - sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Month: - sysTime.wMonth = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Day: - sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Hours: - sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Minutes: - sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Seconds: - sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_TimeZone: - // TODO: do something with this in the future, but this should only hit outside of the while loop. - break; - - default: - break; - } - - pwzStart = pwzEnd; - timeParser = (TIME_PARSERRFC3339)((int)timeParser + 1); - } - - ++pwzEnd; - } - - - if (!::SystemTimeToFileTime(&sysTime, pFileTime)) - { - TimeExitWithLastError(hr, "Failed to convert system time to file time."); - } - -LExit: - ReleaseStr(pwzTime); - - return hr; -} -/**************************************************************************** -TimeCurrentTime - gets the current time in string format - -****************************************************************************/ -extern "C" HRESULT DAPI TimeCurrentTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ) -{ - SYSTEMTIME st; - - if (fGMT) - { - ::GetSystemTime(&st); - } - else - { - SYSTEMTIME stGMT; - TIME_ZONE_INFORMATION tzi; - - ::GetTimeZoneInformation(&tzi); - ::GetSystemTime(&stGMT); - ::SystemTimeToTzSpecificLocalTime(&tzi, &stGMT, &st); - } - - return StrAllocFormatted(ppwz, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); -} - - -/**************************************************************************** -TimeCurrentDateTime - gets the current date and time in string format, - per format described in RFC 3339 -****************************************************************************/ -extern "C" HRESULT DAPI TimeCurrentDateTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ) -{ - SYSTEMTIME st; - - ::GetSystemTime(&st); - - return TimeSystemDateTime(ppwz, &st, fGMT); -} - - -/**************************************************************************** -TimeSystemDateTime - converts the provided system time struct to string format, - per format described in RFC 3339 -****************************************************************************/ -extern "C" HRESULT DAPI TimeSystemDateTime( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME *pst, - __in BOOL fGMT - ) -{ - DWORD dwAbsBias = 0; - - if (fGMT) - { - return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02huZ", pst->wYear, pst->wMonth, pst->wDay, pst->wHour, pst->wMinute, pst->wSecond); - } - else - { - SYSTEMTIME st; - TIME_ZONE_INFORMATION tzi; - - ::GetTimeZoneInformation(&tzi); - ::SystemTimeToTzSpecificLocalTime(&tzi, pst, &st); - dwAbsBias = abs(tzi.Bias); - - 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); - } -} - - -/**************************************************************************** -TimeSystemToDateTimeString - converts the provided system time struct to - string format representing date and time for the specified locale -****************************************************************************/ -HRESULT DAPI TimeSystemToDateTimeString( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME* pst, - __in LCID locale - ) -{ - HRESULT hr = S_OK; - const WCHAR * DATE_FORMAT = L"MMM dd',' yyyy',' "; - const WCHAR * TIME_FORMAT = L"hh':'mm':'ss tt"; - int iLenDate = 0; - int iLenTime = 0; - - iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0); - if (0 >= iLenDate) - { - TimeExitWithLastError(hr, "Failed to get date format with NULL"); - } - - iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0); - if (0 >= iLenTime) - { - TimeExitWithLastError(hr, "Failed to get time format with NULL"); - } - - // Between both lengths we account for 2 null terminators, and only need one, so we subtract one - hr = StrAlloc(ppwz, iLenDate + iLenTime - 1); - TimeExitOnFailure(hr, "Failed to allocate string"); - - if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate)) - { - TimeExitWithLastError(hr, "Failed to get date format with buffer"); - } - // Space to separate them - (*ppwz)[iLenDate - 1] = ' '; - - if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime)) - { - TimeExitWithLastError(hr, "Failed to get time format with buffer"); - } - -LExit: - return hr; -} - -/******************************************************************** - DayFromString - converts string to day - -*******************************************************************/ -static HRESULT DayFromString( - __in_z LPCWSTR wzDay, - __out WORD* pwDayOfWeek - ) -{ - HRESULT hr = E_INVALIDARG; // assume we won't find a matching name - - for (WORD i = 0; i < countof(DAY_OF_WEEK); ++i) - { - if (0 == lstrcmpW(wzDay, DAY_OF_WEEK[i])) - { - *pwDayOfWeek = i; - hr = S_OK; - break; - } - } - - return hr; -} - - -/******************************************************************** - MonthFromString - converts string to month - -*******************************************************************/ -static HRESULT MonthFromString( - __in_z LPCWSTR wzMonth, - __out WORD* pwMonthOfYear - ) -{ - HRESULT hr = E_INVALIDARG; // assume we won't find a matching name - - for (WORD i = 0; i < countof(MONTH_OF_YEAR); ++i) - { - if (0 == lstrcmpW(wzMonth, MONTH_OF_YEAR[i])) - { - *pwMonthOfYear = i; - hr = S_OK; - break; - } - } - - return hr; -} diff --git a/src/dutil/uncutil.cpp b/src/dutil/uncutil.cpp deleted file mode 100644 index 415ea198..00000000 --- a/src/dutil/uncutil.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define UncExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) -#define UncExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) -#define UncExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) -#define UncExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) -#define UncExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_UNCUTIL, e, x, s, __VA_ARGS__) -#define UncExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_UNCUTIL, g, x, s, __VA_ARGS__) - -DAPI_(HRESULT) UncConvertFromMountedDrive( - __inout LPWSTR *psczUNCPath, - __in LPCWSTR sczMountedDrivePath - ) -{ - HRESULT hr = S_OK; - DWORD dwLength = 0; - DWORD er = ERROR_SUCCESS; - LPWSTR sczDrive = NULL; - - // Only copy drive letter and colon - hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2); - UncExitOnFailure(hr, "Failed to copy drive"); - - // ERROR_NOT_CONNECTED means it's not a mapped drive - er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength); - if (ERROR_MORE_DATA == er) - { - er = ERROR_SUCCESS; - - hr = StrAlloc(psczUNCPath, dwLength); - UncExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength); - - er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength); - if (ERROR_CONNECTION_UNAVAIL == er) - { - // 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 - er = ERROR_SUCCESS; - } - UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive); - - // Skip drive letter and colon - hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0); - UncExitOnFailure(hr, "Failed to copy rest of database path"); - } - else - { - if (ERROR_SUCCESS == er) - { - er = ERROR_NO_DATA; - } - - UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive); - } - -LExit: - ReleaseStr(sczDrive); - - return hr; -} diff --git a/src/dutil/uriutil.cpp b/src/dutil/uriutil.cpp deleted file mode 100644 index 7913c22e..00000000 --- a/src/dutil/uriutil.cpp +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define UriExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) -#define UriExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) -#define UriExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) -#define UriExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) -#define UriExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_URIUTIL, e, x, s, __VA_ARGS__) -#define UriExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_URIUTIL, g, x, s, __VA_ARGS__) - - -// -// UriCanonicalize - canonicalizes a URI. -// -extern "C" HRESULT DAPI UriCanonicalize( - __inout_z LPWSTR* psczUri - ) -{ - HRESULT hr = S_OK; - WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; - DWORD cch = countof(wz); - - if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE)) - { - UriExitWithLastError(hr, "Failed to canonicalize URI."); - } - - hr = StrAllocString(psczUri, wz, cch); - UriExitOnFailure(hr, "Failed copy canonicalized URI."); - -LExit: - return hr; -} - - -// -// UriCrack - cracks a URI into constituent parts. -// -extern "C" HRESULT DAPI UriCrack( - __in_z LPCWSTR wzUri, - __out_opt INTERNET_SCHEME* pScheme, - __deref_opt_out_z LPWSTR* psczHostName, - __out_opt INTERNET_PORT* pPort, - __deref_opt_out_z LPWSTR* psczUser, - __deref_opt_out_z LPWSTR* psczPassword, - __deref_opt_out_z LPWSTR* psczPath, - __deref_opt_out_z LPWSTR* psczQueryString - ) -{ - HRESULT hr = S_OK; - URL_COMPONENTSW components = { }; - WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; - WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1]; - WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1]; - WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1]; - - components.dwStructSize = sizeof(URL_COMPONENTSW); - - if (psczHostName) - { - components.lpszHostName = wzHostName; - components.dwHostNameLength = countof(wzHostName); - } - - if (psczUser) - { - components.lpszUserName = wzUserName; - components.dwUserNameLength = countof(wzUserName); - } - - if (psczPassword) - { - components.lpszPassword = wzPassword; - components.dwPasswordLength = countof(wzPassword); - } - - if (psczPath) - { - components.lpszUrlPath = wzPath; - components.dwUrlPathLength = countof(wzPath); - } - - if (psczQueryString) - { - components.lpszExtraInfo = wzQueryString; - components.dwExtraInfoLength = countof(wzQueryString); - } - - if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components)) - { - UriExitWithLastError(hr, "Failed to crack URI."); - } - - if (pScheme) - { - *pScheme = components.nScheme; - } - - if (psczHostName) - { - hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength); - UriExitOnFailure(hr, "Failed to copy host name."); - } - - if (pPort) - { - *pPort = components.nPort; - } - - if (psczUser) - { - hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength); - UriExitOnFailure(hr, "Failed to copy user name."); - } - - if (psczPassword) - { - hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength); - UriExitOnFailure(hr, "Failed to copy password."); - } - - if (psczPath) - { - hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength); - UriExitOnFailure(hr, "Failed to copy path."); - } - - if (psczQueryString) - { - hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength); - UriExitOnFailure(hr, "Failed to copy query string."); - } - -LExit: - return hr; -} - - -// -// UriCrackEx - cracks a URI into URI_INFO. -// -extern "C" HRESULT DAPI UriCrackEx( - __in_z LPCWSTR wzUri, - __in URI_INFO* pUriInfo - ) -{ - HRESULT hr = S_OK; - - hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString); - UriExitOnFailure(hr, "Failed to crack URI."); - -LExit: - return hr; -} - - -// -// UriInfoUninitialize - frees the memory in a URI_INFO struct. -// -extern "C" void DAPI UriInfoUninitialize( - __in URI_INFO* pUriInfo - ) -{ - ReleaseStr(pUriInfo->sczHostName); - ReleaseStr(pUriInfo->sczUser); - ReleaseStr(pUriInfo->sczPassword); - ReleaseStr(pUriInfo->sczPath); - ReleaseStr(pUriInfo->sczQueryString); - memset(pUriInfo, 0, sizeof(URI_INFO)); -} - - -// -// UriCreate - creates a URI from constituent parts. -// -extern "C" HRESULT DAPI UriCreate( - __inout_z LPWSTR* psczUri, - __in INTERNET_SCHEME scheme, - __in_z_opt LPWSTR wzHostName, - __in INTERNET_PORT port, - __in_z_opt LPWSTR wzUser, - __in_z_opt LPWSTR wzPassword, - __in_z_opt LPWSTR wzPath, - __in_z_opt LPWSTR wzQueryString - ) -{ - HRESULT hr = S_OK; - WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; - DWORD cch = countof(wz); - URL_COMPONENTSW components = { }; - - components.dwStructSize = sizeof(URL_COMPONENTSW); - components.nScheme = scheme; - components.lpszHostName = wzHostName; - components.nPort = port; - components.lpszUserName = wzUser; - components.lpszPassword = wzPassword; - components.lpszUrlPath = wzPath; - components.lpszExtraInfo = wzQueryString; - - if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch)) - { - UriExitWithLastError(hr, "Failed to create URI."); - } - - hr = StrAllocString(psczUri, wz, cch); - UriExitOnFailure(hr, "Failed copy created URI."); - -LExit: - return hr; -} - - -// -// UriGetServerAndResource - gets the server and resource as independent strings from a URI. -// -// NOTE: This function is useful for the InternetConnect/HttpRequest APIs. -// -extern "C" HRESULT DAPI UriGetServerAndResource( - __in_z LPCWSTR wzUri, - __out_z LPWSTR* psczServer, - __out_z LPWSTR* psczResource - ) -{ - HRESULT hr = S_OK; - INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN; - LPWSTR sczHostName = NULL; - INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER; - LPWSTR sczUser = NULL; - LPWSTR sczPassword = NULL; - LPWSTR sczPath = NULL; - LPWSTR sczQueryString = NULL; - - hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString); - UriExitOnFailure(hr, "Failed to crack URI."); - - hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL); - UriExitOnFailure(hr, "Failed to allocate server URI."); - - hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString); - UriExitOnFailure(hr, "Failed to allocate resource URI."); - -LExit: - ReleaseStr(sczQueryString); - ReleaseStr(sczPath); - ReleaseStr(sczPassword); - ReleaseStr(sczUser); - ReleaseStr(sczHostName); - - return hr; -} - - -// -// UriFile - returns the file part of the URI. -// -extern "C" HRESULT DAPI UriFile( - __deref_out_z LPWSTR* psczFile, - __in_z LPCWSTR wzUri - ) -{ - HRESULT hr = S_OK; - WCHAR wz[MAX_PATH + 1]; - DWORD cch = countof(wz); - URL_COMPONENTSW uc = { }; - - uc.dwStructSize = sizeof(uc); - uc.lpszUrlPath = wz; - uc.dwUrlPathLength = cch; - - if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc)) - { - UriExitWithLastError(hr, "Failed to crack URI."); - } - - // Copy only the file name. Fortunately, PathFile() understands that - // forward slashes can be directory separators like backslashes. - hr = StrAllocString(psczFile, PathFile(wz), 0); - UriExitOnFailure(hr, "Failed to copy file name"); - -LExit: - return hr; -} - - -/******************************************************************* - UriProtocol - determines the protocol of a URI. - -********************************************************************/ -extern "C" HRESULT DAPI UriProtocol( - __in_z LPCWSTR wzUri, - __out URI_PROTOCOL* pProtocol - ) -{ - Assert(wzUri && *wzUri); - Assert(pProtocol); - - HRESULT hr = S_OK; - - if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && - (L't' == wzUri[1] || L'T' == wzUri[1]) && - (L't' == wzUri[2] || L'T' == wzUri[2]) && - (L'p' == wzUri[3] || L'P' == wzUri[3]) && - (L's' == wzUri[4] || L'S' == wzUri[4]) && - L':' == wzUri[5] && - L'/' == wzUri[6] && - L'/' == wzUri[7]) - { - *pProtocol = URI_PROTOCOL_HTTPS; - } - else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && - (L't' == wzUri[1] || L'T' == wzUri[1]) && - (L't' == wzUri[2] || L'T' == wzUri[2]) && - (L'p' == wzUri[3] || L'P' == wzUri[3]) && - L':' == wzUri[4] && - L'/' == wzUri[5] && - L'/' == wzUri[6]) - { - *pProtocol = URI_PROTOCOL_HTTP; - } - else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && - (L't' == wzUri[1] || L'T' == wzUri[1]) && - (L'p' == wzUri[2] || L'P' == wzUri[2]) && - L':' == wzUri[3] && - L'/' == wzUri[4] && - L'/' == wzUri[5]) - { - *pProtocol = URI_PROTOCOL_FTP; - } - else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && - (L'i' == wzUri[1] || L'I' == wzUri[1]) && - (L'l' == wzUri[2] || L'L' == wzUri[2]) && - (L'e' == wzUri[3] || L'E' == wzUri[3]) && - L':' == wzUri[4] && - L'/' == wzUri[5] && - L'/' == wzUri[6]) - { - *pProtocol = URI_PROTOCOL_FILE; - } - else - { - *pProtocol = URI_PROTOCOL_UNKNOWN; - } - - return hr; -} - - -/******************************************************************* - UriRoot - returns the root of the path specified in the URI. - - examples: - file:///C:\path\path -> C:\ - file://server/share/path/path -> \\server\share - http://www.example.com/path/path -> http://www.example.com/ - ftp://ftp.example.com/path/path -> ftp://www.example.com/ - - NOTE: This function should only be used on cannonicalized URIs. - It does not cannonicalize itself. -********************************************************************/ -extern "C" HRESULT DAPI UriRoot( - __in_z LPCWSTR wzUri, - __out LPWSTR* ppwzRoot, - __out_opt URI_PROTOCOL* pProtocol - ) -{ - Assert(wzUri && *wzUri); - Assert(ppwzRoot); - - HRESULT hr = S_OK; - URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; - LPCWSTR pwcSlash = NULL; - - hr = UriProtocol(wzUri, &protocol); - UriExitOnFailure(hr, "Invalid URI."); - - switch (protocol) - { - case URI_PROTOCOL_FILE: - if (L'/' == wzUri[7]) // file path - { - if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9]) - { - hr = StrAlloc(ppwzRoot, 4); - UriExitOnFailure(hr, "Failed to allocate string for root of URI."); - *ppwzRoot[0] = wzUri[8]; - *ppwzRoot[1] = L':'; - *ppwzRoot[2] = L'\\'; - *ppwzRoot[3] = L'\0'; - } - else - { - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Invalid file path in URI."); - } - } - else // UNC share - { - pwcSlash = wcschr(wzUri + 8, L'/'); - if (!pwcSlash) - { - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Invalid server name in URI."); - } - else - { - hr = StrAllocString(ppwzRoot, L"\\\\", 64); - UriExitOnFailure(hr, "Failed to allocate string for root of URI."); - - pwcSlash = wcschr(pwcSlash + 1, L'/'); - if (pwcSlash) - { - hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8); - UriExitOnFailure(hr, "Failed to add server/share to root of URI."); - } - else - { - hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0); - UriExitOnFailure(hr, "Failed to add server/share to root of URI."); - } - - // replace all slashes with backslashes to be truly UNC. - for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc) - { - if (L'/' == *pwc) - { - *pwc = L'\\'; - } - } - } - } - break; - - case URI_PROTOCOL_FTP: - pwcSlash = wcschr(wzUri + 6, L'/'); - if (pwcSlash) - { - hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - else - { - hr = StrAllocString(ppwzRoot, wzUri, 0); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - break; - - case URI_PROTOCOL_HTTP: - pwcSlash = wcschr(wzUri + 7, L'/'); - if (pwcSlash) - { - hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - else - { - hr = StrAllocString(ppwzRoot, wzUri, 0); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - break; - - default: - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Unknown URI protocol."); - } - - if (pProtocol) - { - *pProtocol = protocol; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI UriResolve( - __in_z LPCWSTR wzUri, - __in_opt LPCWSTR wzBaseUri, - __out LPWSTR* ppwzResolvedUri, - __out_opt URI_PROTOCOL* pResolvedProtocol - ) -{ - UNREFERENCED_PARAMETER(wzUri); - UNREFERENCED_PARAMETER(wzBaseUri); - UNREFERENCED_PARAMETER(ppwzResolvedUri); - UNREFERENCED_PARAMETER(pResolvedProtocol); - - HRESULT hr = E_NOTIMPL; -#if 0 - URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; - - hr = UriProtocol(wzUri, &protocol); - UriExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); - - UriExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); - - if (URI_PROTOCOL_UNKNOWN == protocol) - { - UriExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); - - if (L'/' == *wzUri || L'\\' == *wzUri) - { - hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol); - UriExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); - - hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); - UriExitOnFailure(hr, "Failed to concat file to base URI."); - } - else - { - hr = UriProtocol(wzBaseUri, &protocol); - UriExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); - - LPCWSTR pwcFile = const_cast (UriFile(wzBaseUri)); - if (!pwcFile) - { - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); - } - - hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri); - UriExitOnFailure(hr, "Failed to allocate string for resolved URI."); - - hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); - UriExitOnFailure(hr, "Failed to concat file to resolved URI."); - } - } - else - { - hr = StrAllocString(ppwzResolvedUri, wzUri, 0); - UriExitOnFailure(hr, "Failed to copy resolved URI."); - } - - if (pResolvedProtocol) - { - *pResolvedProtocol = protocol; - } - -LExit: -#endif - return hr; -} diff --git a/src/dutil/userutil.cpp b/src/dutil/userutil.cpp deleted file mode 100644 index ca6d5480..00000000 --- a/src/dutil/userutil.cpp +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// UserExit macros -#define UserExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) -#define UserExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) -#define UserExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) -#define UserExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) -#define UserExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_USERUTIL, e, x, s, __VA_ARGS__) -#define UserExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_USERUTIL, g, x, s, __VA_ARGS__) - -static BOOL CheckIsMemberHelper( - __in_z LPCWSTR pwzGroupUserDomain, - __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, - __in DWORD cguiGroupData - ); - -/******************************************************************* - UserBuildDomainUserName - builds a DOMAIN\USERNAME string - -********************************************************************/ -extern "C" HRESULT DAPI UserBuildDomainUserName( - __out_ecount_z(cchDest) LPWSTR wzDest, - __in int cchDest, - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain - ) -{ - HRESULT hr = S_OK; - DWORD cchLeft = cchDest; - WCHAR* pwz = wzDest; - DWORD cchWz = cchDest; - DWORD cch; - - cch = lstrlenW(pwzDomain); - if (cch >= cchLeft) - { - hr = ERROR_MORE_DATA; - UserExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain); - } - else if (cch > 0) - { - // handle the domain case - - hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0' - UserExitOnFailure(hr, "Failed to copy Domain onto string."); - - cchLeft -= cch; - pwz += cch; - cchWz -= cch; - - if (1 >= cchLeft) - { - hr = ERROR_MORE_DATA; - UserExitOnFailure(hr, "Insufficient buffer size while building domain user name"); - } - - hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0' - UserExitOnFailure(hr, "Failed to copy backslash onto string."); - - --cchLeft; - ++pwz; - --cchWz; - } - - cch = lstrlenW(pwzName); - if (cch >= cchLeft) - { - hr = ERROR_MORE_DATA; - UserExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName); - } - - hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0' - UserExitOnFailure(hr, "Failed to copy User name onto string."); - -LExit: - return hr; -} - - -/******************************************************************* - Checks whether a user is a member of a group - outputs the result via lpfMember -********************************************************************/ -extern "C" HRESULT DAPI UserCheckIsMember( - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain, - __in_z LPCWSTR pwzGroupName, - __in_z LPCWSTR pwzGroupDomain, - __out LPBOOL lpfMember - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - - DWORD dwRead = 0; - DWORD dwTotal = 0; - LPCWSTR wz = NULL; - GROUP_USERS_INFO_0 *pguiGroupData = NULL; - WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME - WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME - BSTR bstrUser = NULL; - BSTR bstrGroup = NULL; - - IADsGroup *pGroup = NULL; - VARIANT_BOOL vtBoolResult = VARIANT_FALSE; - - hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain); - UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); - - hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain); - UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); - - if (pwzDomain && *pwzDomain) - { - wz = pwzDomain; - } - - er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); - // Ignore these errors, and just go to the fallback checks - if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er) - { - 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)); - er = ERROR_SUCCESS; - } - UserExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName); - - if (dwRead != dwTotal) - { - hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); - UserExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName); - } - - if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - - if (NULL != pguiGroupData) - { - ::NetApiBufferFree(pguiGroupData); - pguiGroupData = NULL; - } - - // If we fail with the global groups, try again with the local groups - er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); - // Ignore these errors, and just go to the fallback checks - if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er) - { - 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)); - er = ERROR_SUCCESS; - } - UserExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName); - - if (dwRead != dwTotal) - { - hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); - UserExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName); - } - - if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - - // If the above methods failed, let's try active directory - hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser); - UserExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain); - - hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup); - UserExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain); - - if (lstrlenW(pwzGroupDomain) > 0) - { - hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); - - hr = pGroup->IsMember(bstrUser, &vtBoolResult); - UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); - } - - if (vtBoolResult) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - - hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); - - hr = pGroup->IsMember(bstrUser, &vtBoolResult); - UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); - - if (vtBoolResult) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - -LExit: - ReleaseObject(pGroup); - ReleaseBSTR(bstrUser); - ReleaseBSTR(bstrGroup); - - if (NULL != pguiGroupData) - { - ::NetApiBufferFree(pguiGroupData); - } - - return hr; -} - - -/******************************************************************* - Takes a domain and name, and allocates a BSTR which represents - DOMAIN\NAME's active directory path. The BSTR this function returns - should be released manually using the ReleaseBSTR() macro. -********************************************************************/ -extern "C" HRESULT DAPI UserCreateADsPath( - __in_z LPCWSTR wzObjectDomain, - __in_z LPCWSTR wzObjectName, - __out BSTR *pbstrAdsPath - ) -{ - Assert(wzObjectDomain && wzObjectName && *wzObjectName); - - HRESULT hr = S_OK; - LPWSTR pwzAdsPath = NULL; - - hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0); - UserExitOnFailure(hr, "failed to allocate AdsPath string"); - - if (*wzObjectDomain) - { - hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName); - UserExitOnFailure(hr, "failed to allocate AdsPath string"); - } - else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/")) - { - hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); - UserExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName); - } - else - { - hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0); - UserExitOnFailure(hr, "failed to concat LocalHost/"); - - hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); - UserExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName); - } - - *pbstrAdsPath = ::SysAllocString(pwzAdsPath); - if (NULL == *pbstrAdsPath) - { - hr = E_OUTOFMEMORY; - } - -LExit: - ReleaseStr(pwzAdsPath); - - return hr; -} - - -/******************************************************************* - Helper function to check if pwzGroupUserDomain (in form of "domain\username" is - a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the - output from both NetUserGetGroups() and NetUserGetLocalGroups() -********************************************************************/ -static BOOL CheckIsMemberHelper( - __in_z LPCWSTR pwzGroupUserDomain, - __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, - __in DWORD cguiGroupData - ) -{ - if (NULL == pguiGroupData) - { - return FALSE; - } - - for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter) - { - // If the user is a member of the group, set the output flag to true - if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name)) - { - return TRUE; - } - } - - return FALSE; -} diff --git a/src/dutil/verutil.cpp b/src/dutil/verutil.cpp deleted file mode 100644 index 21626f94..00000000 --- a/src/dutil/verutil.cpp +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) -#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) -#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) -#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) -#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__) - -// constants -const DWORD GROW_RELEASE_LABELS = 3; - -// Forward declarations. -static int CompareDword( - __in const DWORD& dw1, - __in const DWORD& dw2 - ); -static HRESULT CompareReleaseLabel( - __in const VERUTIL_VERSION_RELEASE_LABEL* p1, - __in LPCWSTR wzVersion1, - __in const VERUTIL_VERSION_RELEASE_LABEL* p2, - __in LPCWSTR wzVersion2, - __out int* pnResult - ); -static HRESULT CompareVersionSubstring( - __in LPCWSTR wzString1, - __in int cchCount1, - __in LPCWSTR wzString2, - __in int cchCount2, - __out int* pnResult - ); - - -DAPI_(HRESULT) VerCompareParsedVersions( - __in_opt VERUTIL_VERSION* pVersion1, - __in_opt VERUTIL_VERSION* pVersion2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - DWORD cMaxReleaseLabels = 0; - BOOL fCompareMetadata = FALSE; - - if (pVersion1 && !pVersion1->sczVersion || - pVersion2 && !pVersion2->sczVersion) - { - ExitFunction1(hr = E_INVALIDARG); - } - - if (pVersion1 == pVersion2) - { - ExitFunction1(nResult = 0); - } - else if (pVersion1 && !pVersion2) - { - ExitFunction1(nResult = 1); - } - else if (!pVersion1 && pVersion2) - { - ExitFunction1(nResult = -1); - } - - nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor); - if (0 != nResult) - { - ExitFunction(); - } - - nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor); - if (0 != nResult) - { - ExitFunction(); - } - - nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch); - if (0 != nResult) - { - ExitFunction(); - } - - nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision); - if (0 != nResult) - { - ExitFunction(); - } - - if (pVersion1->cReleaseLabels) - { - if (pVersion2->cReleaseLabels) - { - cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels); - } - else - { - ExitFunction1(nResult = -1); - } - } - else if (pVersion2->cReleaseLabels) - { - ExitFunction1(nResult = 1); - } - - if (cMaxReleaseLabels) - { - for (DWORD i = 0; i < cMaxReleaseLabels; ++i) - { - VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL; - VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL; - - hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult); - if (FAILED(hr) || 0 != nResult) - { - ExitFunction(); - } - } - } - - if (pVersion1->fInvalid) - { - if (!pVersion2->fInvalid) - { - ExitFunction1(nResult = -1); - } - else - { - fCompareMetadata = TRUE; - } - } - else if (pVersion2->fInvalid) - { - ExitFunction1(nResult = 1); - } - - if (fCompareMetadata) - { - hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult); - } - -LExit: - *pnResult = nResult; - return hr; -} - -DAPI_(HRESULT) VerCompareStringVersions( - __in_z LPCWSTR wzVersion1, - __in_z LPCWSTR wzVersion2, - __in BOOL fStrict, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - int nResult = 0; - - hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1); - VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2); - VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2); - - hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); - VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2); - -LExit: - *pnResult = nResult; - - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - - return hr; -} - -DAPI_(HRESULT) VerCopyVersion( - __in VERUTIL_VERSION* pSource, - __out VERUTIL_VERSION** ppVersion - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pCopy = NULL; - - pCopy = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); - VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy."); - - hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0); - VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion); - - pCopy->dwMajor = pSource->dwMajor; - pCopy->dwMinor = pSource->dwMinor; - pCopy->dwPatch = pSource->dwPatch; - pCopy->dwRevision = pSource->dwRevision; - - if (pSource->cReleaseLabels) - { - hr = MemEnsureArraySize(reinterpret_cast(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels); - VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies."); - - pCopy->cReleaseLabels = pSource->cReleaseLabels; - - for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i) - { - VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i; - VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i; - - pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset; - pCopyLabel->cchLabel = pSourceLabel->cchLabel; - pCopyLabel->fNumeric = pSourceLabel->fNumeric; - pCopyLabel->dwValue = pSourceLabel->dwValue; - } - } - - pCopy->cchMetadataOffset = pSource->cchMetadataOffset; - pCopy->fInvalid = pSource->fInvalid; - - *ppVersion = pCopy; - pCopy = NULL; - -LExit: - ReleaseVerutilVersion(pCopy); - - return hr; -} - -DAPI_(void) VerFreeVersion( - __in VERUTIL_VERSION* pVersion - ) -{ - if (pVersion) - { - ReleaseStr(pVersion->sczVersion); - ReleaseMem(pVersion->rgReleaseLabels); - ReleaseMem(pVersion); - } -} - -DAPI_(HRESULT) VerParseVersion( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __in BOOL fStrict, - __out VERUTIL_VERSION** ppVersion - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - LPCWSTR wzEnd = NULL; - LPCWSTR wzPartBegin = NULL; - LPCWSTR wzPartEnd = NULL; - BOOL fInvalid = FALSE; - BOOL fLastPart = FALSE; - BOOL fTrailingDot = FALSE; - BOOL fParsedVersionNumber = FALSE; - BOOL fExpectedReleaseLabels = FALSE; - DWORD iPart = 0; - - if (!wzVersion || !ppVersion) - { - ExitFunction1(hr = E_INVALIDARG); - } - - // Get string length if not provided. - if (!cchVersion) - { - hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); - VerExitOnRootFailure(hr, "Failed to get length of version string: %ls", wzVersion); - } - else if (INT_MAX < cchVersion) - { - VerExitOnRootFailure(hr = E_INVALIDARG, "Version string is too long: %Iu", cchVersion); - } - - if (L'v' == *wzVersion || L'V' == *wzVersion) - { - ++wzVersion; - --cchVersion; - } - - pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); - VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion); - - hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion); - VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion); - - wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion; - - // Save end pointer. - wzEnd = wzVersion + cchVersion; - - // Parse version number - while (wzPartBegin < wzEnd) - { - fTrailingDot = FALSE; - - // Find end of part. - for (;;) - { - if (wzPartEnd >= wzEnd) - { - fLastPart = TRUE; - break; - } - - switch (*wzPartEnd) - { - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - ++wzPartEnd; - continue; - case L'.': - fTrailingDot = TRUE; - break; - case L'-': - case L'+': - fLastPart = TRUE; - break; - default: - fInvalid = TRUE; - break; - } - - break; - } - - if (wzPartBegin == wzPartEnd) - { - fInvalid = TRUE; - } - - if (fInvalid) - { - break; - } - - DWORD cchPart = 0; - hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); - if (FAILED(hr)) - { - fInvalid = TRUE; - break; - } - - // Parse version part. - UINT uPart = 0; - hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart); - if (FAILED(hr)) - { - fInvalid = TRUE; - break; - } - - switch (iPart) - { - case 0: - pVersion->dwMajor = uPart; - break; - case 1: - pVersion->dwMinor = uPart; - break; - case 2: - pVersion->dwPatch = uPart; - break; - case 3: - pVersion->dwRevision = uPart; - break; - } - - if (fTrailingDot) - { - ++wzPartEnd; - } - wzPartBegin = wzPartEnd; - ++iPart; - - if (4 <= iPart || fLastPart) - { - fParsedVersionNumber = TRUE; - break; - } - } - - fInvalid |= !fParsedVersionNumber || fTrailingDot; - - if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-') - { - wzPartBegin = wzPartEnd = wzPartBegin + 1; - fExpectedReleaseLabels = TRUE; - fLastPart = FALSE; - } - - while (fExpectedReleaseLabels && wzPartBegin < wzEnd) - { - fTrailingDot = FALSE; - - // Find end of part. - for (;;) - { - if (wzPartEnd >= wzEnd) - { - fLastPart = TRUE; - break; - } - - if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' || - *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' || - *wzPartEnd >= L'a' && *wzPartEnd <= L'z' || - *wzPartEnd == L'-') - { - ++wzPartEnd; - continue; - } - else if (*wzPartEnd == L'+') - { - fLastPart = TRUE; - } - else if (*wzPartEnd == L'.') - { - fTrailingDot = TRUE; - } - else - { - fInvalid = TRUE; - } - - break; - } - - if (wzPartBegin == wzPartEnd) - { - fInvalid = TRUE; - } - - if (fInvalid) - { - break; - } - - int cchLabel = 0; - hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel); - if (FAILED(hr) || 0 > cchLabel) - { - fInvalid = TRUE; - break; - } - - hr = MemReAllocArray(reinterpret_cast(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS)); - VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion); - - VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels; - ++pVersion->cReleaseLabels; - - // Try to parse as number. - UINT uLabel = 0; - hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel); - if (SUCCEEDED(hr)) - { - pReleaseLabel->fNumeric = TRUE; - pReleaseLabel->dwValue = uLabel; - } - - pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion; - pReleaseLabel->cchLabel = cchLabel; - - if (fTrailingDot) - { - ++wzPartEnd; - } - wzPartBegin = wzPartEnd; - - if (fLastPart) - { - break; - } - } - - fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot); - - if (!fInvalid && wzPartBegin < wzEnd) - { - if (*wzPartBegin == L'+') - { - wzPartBegin = wzPartEnd = wzPartBegin + 1; - } - else - { - fInvalid = TRUE; - } - } - - if (fInvalid && fStrict) - { - ExitFunction1(hr = E_INVALIDARG); - } - - pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion; - pVersion->fInvalid = fInvalid; - - *ppVersion = pVersion; - pVersion = NULL; - hr = S_OK; - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - -DAPI_(HRESULT) VerVersionFromQword( - __in DWORD64 qwVersion, - __out VERUTIL_VERSION** ppVersion - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - - pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); - VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD."); - - pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff); - pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff); - pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff); - pVersion->dwRevision = (WORD)(qwVersion & 0xffff); - - hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision); - ExitOnFailure(hr, "Failed to allocate and format the version string."); - - pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion); - - *ppVersion = pVersion; - pVersion = NULL; - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - - -static int CompareDword( - __in const DWORD& dw1, - __in const DWORD& dw2 - ) -{ - int nResult = 0; - - if (dw1 > dw2) - { - nResult = 1; - } - else if (dw1 < dw2) - { - nResult = -1; - } - - return nResult; -} - -static HRESULT CompareReleaseLabel( - __in const VERUTIL_VERSION_RELEASE_LABEL* p1, - __in LPCWSTR wzVersion1, - __in const VERUTIL_VERSION_RELEASE_LABEL* p2, - __in LPCWSTR wzVersion2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - - if (p1 == p2) - { - ExitFunction(); - } - else if (p1 && !p2) - { - ExitFunction1(nResult = 1); - } - else if (!p1 && p2) - { - ExitFunction1(nResult = -1); - } - - if (p1->fNumeric) - { - if (p2->fNumeric) - { - nResult = CompareDword(p1->dwValue, p2->dwValue); - } - else - { - nResult = -1; - } - } - else - { - if (p2->fNumeric) - { - nResult = 1; - } - else - { - hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult); - } - } - -LExit: - *pnResult = nResult; - - return hr; -} - -static HRESULT CompareVersionSubstring( - __in LPCWSTR wzString1, - __in int cchCount1, - __in LPCWSTR wzString2, - __in int cchCount2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - - nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2); - if (!nResult) - { - VerExitOnLastError(hr, "Failed to compare version substrings"); - } - -LExit: - *pnResult = nResult - 2; - - return hr; -} diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp deleted file mode 100644 index 7414ac42..00000000 --- a/src/dutil/wiutil.cpp +++ /dev/null @@ -1,1629 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) -#define WiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) -#define WiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) -#define WiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) -#define WiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WIUTIL, e, x, s, __VA_ARGS__) -#define WiuExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WIUTIL, g, x, s, __VA_ARGS__) - - -// constants - -const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF; -const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64; - - -// structs - - -static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW; -static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW; -static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW; -static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW; -static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW; -static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW; -static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW; -static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW; -static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI; -static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW; -static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW; -static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW; - -// MSI 3.0+ -static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL; -static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL; -static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL; -static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL; -static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL; -static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL; -static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; - -static HMODULE vhMsiDll = NULL; -static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; -static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; -static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL; -static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; -static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; -static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL; -static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL; - -// MSI Transactions v4.5+ -static PFN_MSIBEGINTRANSACTIONW vpfnMsiBeginTransaction = NULL; -static PFN_MSIENDTRANSACTION vpfnMsiEndTransaction = NULL; - -static BOOL vfWiuInitialized = FALSE; - -// globals -static DWORD vdwMsiDllMajorMinor = 0; -static DWORD vdwMsiDllBuildRevision = 0; - - -// internal function declarations - -static DWORD CheckForRestartErrorCode( - __in DWORD dwErrorCode, - __out WIU_RESTART* pRestart - ); -static INT CALLBACK InstallEngineCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_z_opt LPCWSTR wzMessage - ); -static INT CALLBACK InstallEngineRecordCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_opt MSIHANDLE hRecord - ); -static INT HandleInstallMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT HandleInstallProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_z_opt LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT SendMsiMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT SendErrorMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT SendFilesInUseMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_opt MSIHANDLE hRecord, - __in BOOL fRestartManagerRequest - ); -static INT SendProgressUpdate( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ); -static void ResetProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ); -static DWORD CalculatePhaseProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in DWORD dwProgressIndex, - __in DWORD dwWeightPercentage - ); -void InitializeMessageData( - __in_opt MSIHANDLE hRecord, - __deref_out_ecount(*pcData) LPWSTR** prgsczData, - __out DWORD* pcData - ); -void UninitializeMessageData( - __in LPWSTR* rgsczData, - __in DWORD cData - ); - - -/******************************************************************** - WiuInitialize - initializes wiutil - -*********************************************************************/ -extern "C" HRESULT DAPI WiuInitialize( - ) -{ - HRESULT hr = S_OK; - LPWSTR sczMsiDllPath = NULL; - - hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath); - WiuExitOnFailure(hr, "Failed to load Msi.DLL"); - - // Ignore failures - FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision); - - vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW")); - if (NULL == vpfnMsiDeterminePatchSequenceW) - { - vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary; - } - - vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW")); - if (NULL == vpfnMsiDetermineApplicablePatchesW) - { - vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary; - } - - vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW")); - if (NULL == vpfnMsiEnumProductsExW) - { - vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary; - } - - vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW")); - if (NULL == vpfnMsiGetPatchInfoExW) - { - vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary; - } - - vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW")); - if (NULL == vpfnMsiGetProductInfoExW) - { - vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary; - } - - vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord")); - if (NULL == vpfnMsiSetExternalUIRecord) - { - vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary; - } - - //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; - vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW")); - if (NULL == vpfnMsiSourceListAddSourceExW) - { - vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary; - } - - // MSI Transaction functions - if (NULL == vpfnMsiBeginTransaction) - { - vpfnMsiBeginTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiBeginTransactionW")); - } - - if (NULL == vpfnMsiEndTransaction) - { - vpfnMsiEndTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEndTransaction")); - } - - vfWiuInitialized = TRUE; - -LExit: - ReleaseStr(sczMsiDllPath); - return hr; -} - - -/******************************************************************** - WiuUninitialize - uninitializes wiutil - -*********************************************************************/ -extern "C" void DAPI WiuUninitialize( - ) -{ - if (vhMsiDll) - { - ::FreeLibrary(vhMsiDll); - vhMsiDll = NULL; - vpfnMsiSetExternalUIRecordFromLibrary = NULL; - vpfnMsiGetProductInfoExWFromLibrary = NULL; - vpfnMsiGetPatchInfoExWFromLibrary = NULL; - vpfnMsiEnumProductsExWFromLibrary = NULL; - vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; - vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; - vpfnMsiSourceListAddSourceExWFromLibrary = NULL; - vpfnMsiBeginTransaction = NULL; - vpfnMsiEndTransaction = NULL; - } - - vfWiuInitialized = FALSE; -} - - -/******************************************************************** - WiuFunctionOverride - overrides the Windows installer functions. Typically used - for unit testing. - -*********************************************************************/ -extern "C" void DAPI WiuFunctionOverride( - __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, - __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, - __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, - __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, - __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, - __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, - __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, - __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, - __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, - __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, - __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, - __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, - __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW - ) -{ - vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW; - vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW; - vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW; - vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW; - vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW; - vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW; - vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW; - vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI; - vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW; - vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW; - vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary; - vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary; - vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary; -} - - -extern "C" HRESULT DAPI WiuGetComponentPath( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - DWORD cchCompare; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); - if (INSTALLSTATE_MOREDATA == *pInstallState) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); - } - - if (INSTALLSTATE_INVALIDARG == *pInstallState) - { - hr = E_INVALIDARG; - WiuExitOnRootFailure(hr, "Invalid argument when getting component path."); - } - else if (INSTALLSTATE_UNKNOWN == *pInstallState) - { - ExitFunction(); - } - - // If the actual path length is greater than or equal to the original buffer - // allocate a larger buffer and get the path again, just in case we are - // missing any part of the path. - if (cchCompare <= cch) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuLocateComponent( - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - DWORD cchCompare; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); - if (INSTALLSTATE_MOREDATA == *pInstallState) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); - } - - if (INSTALLSTATE_INVALIDARG == *pInstallState) - { - hr = E_INVALIDARG; - WiuExitOnRootFailure(hr, "Invalid argument when locating component."); - } - else if (INSTALLSTATE_UNKNOWN == *pInstallState) - { - ExitFunction(); - } - - // If the actual path length is greater than or equal to the original buffer - // allocate a larger buffer and get the path again, just in case we are - // missing any part of the path. - if (cchCompare <= cch) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuQueryFeatureState( - __in_z LPCWSTR wzProduct, - __in_z LPCWSTR wzFeature, - __out INSTALLSTATE* pInstallState - ) -{ - HRESULT hr = S_OK; - - *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature); - if (INSTALLSTATE_INVALIDARG == *pInstallState) - { - hr = E_INVALIDARG; - WiuExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetProductInfo( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for product info."); - - er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for product info."); - - er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get product info."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetProductInfoEx( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - if (!vpfnMsiGetProductInfoExW) - { - hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue); - WiuExitOnFailure(hr, "Failed to get product info when extended info was not available."); - - ExitFunction(); - } - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for extended product info."); - - er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for extended product info."); - - er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get extended product info."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetProductProperty( - __in MSIHANDLE hProduct, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for product property."); - - er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for product property."); - - er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get product property."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetPatchInfoEx( - __in_z LPCWSTR wzPatchCode, - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - if (!vpfnMsiGetPatchInfoExW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for extended patch info."); - - er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for extended patch info."); - - er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get extended patch info."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuDeterminePatchSequence( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT context, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!vpfnMsiDeterminePatchSequenceW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo); - WiuExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuDetermineApplicablePatches( - __in_z LPCWSTR wzProductPackagePath, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!vpfnMsiDetermineApplicablePatchesW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); - WiuExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuEnumProducts( - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuEnumProductsEx( - __in_z_opt LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in DWORD dwContext, - __in DWORD dwIndex, - __out_opt WCHAR wzInstalledProductCode[39], - __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, - __out_opt LPWSTR wzSid, - __inout_opt LPDWORD pcchSid - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!vpfnMsiEnumProductsExW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuEnumRelatedProducts( - __in_z LPCWSTR wzUpgradeCode, - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - WiuExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); - -LExit: - return hr; -} - -/******************************************************************** - WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code. - - Parameters: - wzUpgradeCode - The upgrade code that will be used to find the related products. - prgsczProductCodes - Pointer to the array that will contain the product codes. - pcRelatedProducts - Returns the count of the number of related products found. - fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found. -********************************************************************/ -extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( - __in_z LPCWSTR wzUpgradeCode, - __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, - __out DWORD* pcRelatedProducts, - __in BOOL fReturnHighestVersionOnly - ) -{ - HRESULT hr = S_OK; - WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { }; - LPWSTR sczInstalledVersion = NULL; - VERUTIL_VERSION* pCurrentVersion = NULL; - VERUTIL_VERSION* pHighestVersion = NULL; - int nCompare = 0; - - // make sure we start at zero - *pcRelatedProducts = 0; - - for (DWORD i = 0; ; ++i) - { - hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode); - - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - break; - } - WiuExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); - - if (fReturnHighestVersionOnly) - { - // try to get the version but if the product registration is broken - // (for whatever reason), skip this product - hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); - if (FAILED(hr)) - { - WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode); - continue; - } - - hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCurrentVersion); - WiuExitOnFailure(hr, "Failed to parse version: %ls for product code: %ls", sczInstalledVersion, wzCurrentProductCode); - - if (pCurrentVersion->fInvalid) - { - WiuExitTrace(E_INVALIDDATA, "Enumerated msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'"); - } - - // if this is the first product found then it is the highest version (for now) - if (!pHighestVersion) - { - pHighestVersion = pCurrentVersion; - pCurrentVersion = NULL; - } - else - { - hr = VerCompareParsedVersions(pCurrentVersion, pHighestVersion, &nCompare); - WiuExitOnFailure(hr, "Failed to compare version '%ls' to highest version: '%ls'", pCurrentVersion->sczVersion, pHighestVersion->sczVersion); - - // if this is the highest version encountered so far then overwrite - // the first item in the array (there will never be more than one item) - if (nCompare > 0) - { - ReleaseVerutilVersion(pHighestVersion); - pHighestVersion = pCurrentVersion; - pCurrentVersion = NULL; - - hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); - WiuExitOnFailure(hr, "Failed to update array with higher versioned product code."); - } - else - { - ReleaseVerutilVersion(pCurrentVersion); - } - - // continue here as we don't want anything else added to the list - continue; - } - } - - hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0); - WiuExitOnFailure(hr, "Failed to add product code to array."); - } - -LExit: - ReleaseVerutilVersion(pCurrentVersion); - ReleaseVerutilVersion(pHighestVersion); - ReleaseStr(sczInstalledVersion); - return hr; -} - - -extern "C" HRESULT DAPI WiuEnableLog( - __in DWORD dwLogMode, - __in_z LPCWSTR wzLogFile, - __in DWORD dwLogAttributes - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes); - WiuExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuInitializeInternalUI( - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ) -{ - HRESULT hr = S_OK; - - memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); - - pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); - pExecuteContext->hwndPreviousParentWindow = hwndParent; - - if (INSTALLUILEVEL_NOCHANGE == pExecuteContext->previousInstallUILevel) - { - hr = E_INVALIDARG; - } - - return hr; -} - - -extern "C" HRESULT DAPI WiuInitializeExternalUI( - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in LPVOID pvContext, - __in BOOL fRollback, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | - INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | - INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | - INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | - INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE; - - if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor) - { - dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE; - } - - // Wire the internal and external UI handler. - hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext); - WiuExitOnFailure(hr, "Failed to set internal UI level and window."); - - pExecuteContext->fRollback = fRollback; - pExecuteContext->pfnMessageHandler = pfnMessageHandler; - pExecuteContext->pvContext = pvContext; - - // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external - // UI handler if necesary. - if (vpfnMsiSetExternalUIRecord) - { - er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord); - WiuExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); - pExecuteContext->fSetPreviousExternalUIRecord = TRUE; - } - else - { - pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext); - pExecuteContext->fSetPreviousExternalUI = TRUE; - } - -LExit: - return hr; -} - - -extern "C" void DAPI WiuUninitializeExternalUI( - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ) -{ - if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel) - { - pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow); - } - - if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler - { - vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL); - } - - if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler - { - vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL); - } - - memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); -} - - -extern "C" HRESULT DAPI WiuConfigureProductEx( - __in_z LPCWSTR wzProduct, - __in int iInstallLevel, - __in INSTALLSTATE eInstallState, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine); - er = CheckForRestartErrorCode(er, pRestart); - WiuExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuInstallProduct( - __in_z LPCWSTR wzPackagePath, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine); - er = CheckForRestartErrorCode(er, pRestart); - WiuExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuRemovePatches( - __in_z LPCWSTR wzPatchList, - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzPropertyList, - __out WIU_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList); - er = CheckForRestartErrorCode(er, pRestart); - WiuExitOnWin32Error(er, hr, "Failed to remove patches."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuSourceListAddSourceEx( - __in_z LPCWSTR wzProductCodeOrPatchCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in DWORD dwCode, - __in_z LPCWSTR wzSource, - __in_opt DWORD dwIndex - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex); - WiuExitOnWin32Error(er, hr, "Failed to add source."); - -LExit: - return hr; -} - -extern "C" BOOL DAPI WiuIsMsiTransactionSupported( - ) -{ - return vpfnMsiBeginTransaction && vpfnMsiEndTransaction; -} - -extern "C" HRESULT DAPI WiuBeginTransaction( - __in_z LPCWSTR szName, - __in DWORD dwTransactionAttributes, - __out MSIHANDLE * phTransactionHandle, - __out HANDLE * phChangeOfOwnerEvent, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!WiuIsMsiTransactionSupported()) - { - WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); - } - - hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); - WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); - - er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent); - WiuExitOnWin32Error(er, hr, "Failed to begin transaction."); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI WiuEndTransaction( - __in DWORD dwTransactionState, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!WiuIsMsiTransactionSupported()) - { - WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); - } - - hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); - WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); - - er = vpfnMsiEndTransaction(dwTransactionState); - WiuExitOnWin32Error(er, hr, "Failed to end transaction."); - -LExit: - return hr; -} - - - -static DWORD CheckForRestartErrorCode( - __in DWORD dwErrorCode, - __out WIU_RESTART* pRestart - ) -{ - switch (dwErrorCode) - { - case ERROR_SUCCESS_REBOOT_REQUIRED: - case ERROR_SUCCESS_RESTART_REQUIRED: - *pRestart = WIU_RESTART_REQUIRED; - dwErrorCode = ERROR_SUCCESS; - break; - - case ERROR_SUCCESS_REBOOT_INITIATED: - case ERROR_INSTALL_SUSPEND: - *pRestart = WIU_RESTART_INITIATED; - dwErrorCode = ERROR_SUCCESS; - break; - } - - return dwErrorCode; -} - -static INT CALLBACK InstallEngineCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_z_opt LPCWSTR wzMessage - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; - INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); - UINT uiFlags = 0x00FFFFFF & uiMessage; - - if (wzMessage) - { - if (INSTALLMESSAGE_PROGRESS == mt) - { - nResult = HandleInstallProgress(pContext, wzMessage, NULL); - } - else - { - nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL); - } - } - - return nResult; -} - -static INT CALLBACK InstallEngineRecordCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - HRESULT hr = S_OK; - WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; - - INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); - UINT uiFlags = 0x00FFFFFF & uiMessage; - LPWSTR sczMessage = NULL; - DWORD cchMessage = 0; - - if (hRecord) - { - if (INSTALLMESSAGE_PROGRESS == mt) - { - nResult = HandleInstallProgress(pContext, NULL, hRecord); - } - else - { - // create formated message string -#pragma prefast(push) -#pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size - DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage); -#pragma prefast(pop) - if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) - { - hr = StrAlloc(&sczMessage, ++cchMessage); - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - WiuExitOnFailure(hr, "Failed to allocate string for formated message."); - - er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage); - WiuExitOnWin32Error(er, hr, "Failed to format message record."); - - // Pass to handler including both the formated message and the original record. - nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord); - } - } - -LExit: - ReleaseStr(sczMessage); - return nResult; -} - -static INT HandleInstallMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - -Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage); - - // Handle the message. - switch (mt) - { - case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data - ResetProgress(pContext); - break; - - case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data - break; - - case INSTALLMESSAGE_ACTIONSTART: - if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; - } - - nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); - break; - - case INSTALLMESSAGE_ACTIONDATA: - if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) - { - if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; - } - else // rollback. - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; - } - - nResult = SendProgressUpdate(pContext); - } - else - { - nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); - } - break; - - case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough; - case INSTALLMESSAGE_FATALEXIT: __fallthrough; - case INSTALLMESSAGE_ERROR: - nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord); - break; - - case INSTALLMESSAGE_FILESINUSE: - case INSTALLMESSAGE_RMFILESINUSE: - nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt); - break; - -/* -#if 0 - case INSTALLMESSAGE_COMMONDATA: - if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2]) - { - if (L'0' == wzMessage[3]) - { - // TODO: handle the language common data message. - lres = IDOK; - return lres; - } - else if (L'1' == wzMessage[3]) - { - // TODO: really handle sending the caption. - lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast(wzMessage + 3)); - return lres; - } - else if (L'2' == wzMessage[3]) - { - // TODO: really handle sending the cancel button status. - lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast(wzMessage + 3)); - return lres; - } - } - break; -#endif -*/ - - //case INSTALLMESSAGE_WARNING: - //case INSTALLMESSAGE_USER: - //case INSTALLMESSAGE_INFO: - //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard - default: - nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); - break; - } - - // Always return "no action" (0) for resolve source messages. - return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult; -} - -static INT HandleInstallProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_z_opt LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - HRESULT hr = S_OK; - INT nResult = IDNOACTION; - INT iFields[4] = { }; - INT cFields = 0; - LPCWSTR pwz = NULL; - DWORD cch = 0; - - // get field values - if (hRecord) - { - cFields = ::MsiRecordGetFieldCount(hRecord); - cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold - for (INT i = 0; i < cFields; ++i) - { - iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1); - } - } - else - { - Assert(wzMessage); - - // parse message string - pwz = wzMessage; - while (cFields < 4) - { - // check if we have the start of a valid part - if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2]) - { - break; - } - pwz += 3; - - // find character count of number - cch = 0; - while (pwz[cch] && L' ' != pwz[cch]) - { - ++cch; - } - - // parse number - hr = StrStringToInt32(pwz, cch, &iFields[cFields]); - WiuExitOnFailure(hr, "Failed to parse MSI message part."); - - // increment field count - ++cFields; - } - } - -#ifdef _DEBUG - WCHAR wz[256]; - ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]); - Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz); -#endif - - // Verify that we have the enough field values. - if (1 > cFields) - { - ExitFunction(); // unknown message, bail - } - - // Handle based on message type. - switch (iFields[0]) - { - case 0: // master progress reset - if (4 > cFields) - { - Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); - ExitFunction(); - } - //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]); - - // Update the index into progress array. - if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) - { - pContext->dwCurrentProgressIndex = 0; - } - else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress)) - { - ++pContext->dwCurrentProgressIndex; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - WiuExitOnRootFailure(hr, "Insufficient space to hold progress information."); - } - - // we only care about the first stage after script execution has started - //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3]) - //{ - // pEngineInfo->fMsiProgressFinished = TRUE; - //} - - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1]; - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]); - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]); - - if (0 == pContext->dwCurrentProgressIndex) - { - // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase - // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress - // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this - // "close" and deal with the rest. - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50; - } - break; - - case 1: // action info. - if (3 > cFields) - { - Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); - ExitFunction(); - } - //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]); - - if (0 == iFields[2]) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; - } - else - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE; - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1]; - } - break; - - case 2: // progress report. - if (2 > cFields) - { - Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); - break; - } - - //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]); - - if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) - { - break; - } - else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal) - { - break; - } - - // Update progress. - if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1]; - } - else // rollback. - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1]; - } - break; - - case 3: // extend the progress bar. - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1]; - break; - - default: - ExitFunction(); // unknown message, bail - } - - // If we have a valid progress index, send an update. - if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex) - { - nResult = SendProgressUpdate(pContext); - } - -LExit: - return nResult; -} - -static INT SendMsiMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_MESSAGE message = { }; - LPWSTR* rgsczData = NULL; - DWORD cData = 0; - - InitializeMessageData(hRecord, &rgsczData, &cData); - - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; - message.dwAllowedResults = uiFlags; - message.cData = cData; - message.rgwzData = (LPCWSTR*)rgsczData; - message.msiMessage.mt = mt; - message.msiMessage.wzMessage = wzMessage; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - UninitializeMessageData(rgsczData, cData); - return nResult; -} - -static INT SendErrorMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_MESSAGE message = { }; - DWORD dwErrorCode = 0; - LPWSTR* rgsczData = NULL; - DWORD cData = 0; - - if (hRecord) - { - dwErrorCode = ::MsiRecordGetInteger(hRecord, 1); - - // Set the recommendation if it's a known error code. - switch (dwErrorCode) - { - case 1605: // continue with install even if there isn't enough room for rollback. - nResult = IDIGNORE; - break; - - case 1704: // rollback suspended installs so our install can continue. - nResult = IDOK; - break; - } - } - - InitializeMessageData(hRecord, &rgsczData, &cData); - - message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; - message.dwAllowedResults = uiFlags; - message.nResultRecommendation = nResult; - message.cData = cData; - message.rgwzData = (LPCWSTR*)rgsczData; - message.error.dwErrorCode = dwErrorCode; - message.error.wzMessage = wzMessage; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - UninitializeMessageData(rgsczData, cData); - return nResult; -} - -static INT SendFilesInUseMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_opt MSIHANDLE hRecord, - __in BOOL /*fRestartManagerRequest*/ - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_MESSAGE message = { }; - LPWSTR* rgsczData = NULL; - DWORD cData = 0; - - InitializeMessageData(hRecord, &rgsczData, &cData); - - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; - message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY; - message.cData = cData; - message.rgwzData = (LPCWSTR*)rgsczData; - message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information. - message.msiFilesInUse.rgwzFiles = message.rgwzData; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - UninitializeMessageData(rgsczData, cData); - return nResult; -} - -static INT SendProgressUpdate( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ) -{ - int nResult = IDNOACTION; - DWORD dwPercentage = 0; // number representing 0 - 100% - WIU_MSI_EXECUTE_MESSAGE message = { }; - - //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal; - //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete); - //double dProgressGauge = 0; - //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal; - - // Calculate progress for the phases of Windows Installer. - // TODO: handle upgrade progress which would add another phase. - dwPercentage += CalculatePhaseProgress(pContext, 0, 15); - dwPercentage += CalculatePhaseProgress(pContext, 1, 80); - dwPercentage += CalculatePhaseProgress(pContext, 2, 5); - dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%. - - if (pContext->fRollback) - { - dwPercentage = 100 - dwPercentage; - } - - //if (qwTotal) // avoid "divide by zero" if the MSI range is blank. - //{ - // // calculate gauge. - // double dProgressGauge = static_cast(qwCompleted) / static_cast(qwTotal); - // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804; - // qwCompleted = (DWORD)(dProgressGauge * qwTotal); - - // // calculate progress within range - // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal)); - // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal); - //} - -#ifdef _DEBUG - DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted; - DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal; - Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage); - //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress."); -#endif - - message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = dwPercentage; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - return nResult; -} - -static void ResetProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ) -{ - memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress)); - pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID; -} - -static DWORD CalculatePhaseProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in DWORD dwProgressIndex, - __in DWORD dwWeightPercentage - ) -{ - DWORD dwPhasePercentage = 0; - - // If we've already passed this progress index, return the maximum percentage possible (the weight) - if (dwProgressIndex < pContext->dwCurrentProgressIndex) - { - dwPhasePercentage = dwWeightPercentage; - } - else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress. - { - WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex; - if (pProgress->dwTotal) - { - DWORD64 dw64Completed = pProgress->dwCompleted; - dwPhasePercentage = static_cast(dw64Completed * dwWeightPercentage / pProgress->dwTotal); - } - } - // else we're not there yet so it has to be zero. - - return dwPhasePercentage; -} - -void InitializeMessageData( - __in_opt MSIHANDLE hRecord, - __deref_out_ecount(*pcData) LPWSTR** prgsczData, - __out DWORD* pcData - ) -{ - DWORD cData = 0; - LPWSTR* rgsczData = NULL; - - // If we have a record based message, try to get the extra data. - if (hRecord) - { - cData = ::MsiRecordGetFieldCount(hRecord); - if (cData) - { - rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE); - } - - for (DWORD i = 0; rgsczData && i < cData; ++i) - { - DWORD cch = 0; - - // get string from record -#pragma prefast(push) -#pragma prefast(disable:6298) - DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch); -#pragma prefast(pop) - if (ERROR_MORE_DATA == er) - { - HRESULT hr = StrAlloc(&rgsczData[i], ++cch); - if (SUCCEEDED(hr)) - { - er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch); - } - } - } - } - - *prgsczData = rgsczData; - *pcData = cData; -} - -void UninitializeMessageData( - __in LPWSTR* rgsczData, - __in DWORD cData - ) -{ - // Clean up if there was any data allocated. - if (rgsczData) - { - for (DWORD i = 0; i < cData; ++i) - { - ReleaseStr(rgsczData[i]); - } - - MemFree(rgsczData); - } -} diff --git a/src/dutil/wuautil.cpp b/src/dutil/wuautil.cpp deleted file mode 100644 index dfb28818..00000000 --- a/src/dutil/wuautil.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define WuaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) -#define WuaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) -#define WuaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) -#define WuaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) -#define WuaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WUAUTIL, e, x, s, __VA_ARGS__) -#define WuaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WUAUTIL, g, x, s, __VA_ARGS__) - - -// internal function declarations - -static HRESULT GetAutomaticUpdatesService( - __out IAutomaticUpdates **ppAutomaticUpdates - ); - - -// function definitions - -extern "C" HRESULT DAPI WuaPauseAutomaticUpdates() -{ - HRESULT hr = S_OK; - IAutomaticUpdates *pAutomaticUpdates = NULL; - - hr = GetAutomaticUpdatesService(&pAutomaticUpdates); - WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); - - hr = pAutomaticUpdates->Pause(); - WuaExitOnFailure(hr, "Failed to pause the Automatic Updates service."); - -LExit: - ReleaseObject(pAutomaticUpdates); - - return hr; -} - -extern "C" HRESULT DAPI WuaResumeAutomaticUpdates() -{ - HRESULT hr = S_OK; - IAutomaticUpdates *pAutomaticUpdates = NULL; - - hr = GetAutomaticUpdatesService(&pAutomaticUpdates); - WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); - - hr = pAutomaticUpdates->Resume(); - WuaExitOnFailure(hr, "Failed to resume the Automatic Updates service."); - -LExit: - ReleaseObject(pAutomaticUpdates); - - return hr; -} - -extern "C" HRESULT DAPI WuaRestartRequired( - __out BOOL* pfRestartRequired - ) -{ - HRESULT hr = S_OK; - ISystemInformation* pSystemInformation = NULL; - VARIANT_BOOL bRestartRequired; - - hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast(&pSystemInformation)); - WuaExitOnRootFailure(hr, "Failed to get WUA system information interface."); - - hr = pSystemInformation->get_RebootRequired(&bRestartRequired); - WuaExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); - - *pfRestartRequired = (VARIANT_FALSE != bRestartRequired); - -LExit: - ReleaseObject(pSystemInformation); - - return hr; -} - - -// internal function definitions - -static HRESULT GetAutomaticUpdatesService( - __out IAutomaticUpdates **ppAutomaticUpdates - ) -{ - HRESULT hr = S_OK; - CLSID clsidAutomaticUpdates = { }; - - hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates); - WuaExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate."); - - hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast(ppAutomaticUpdates)); - WuaExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate."); - -LExit: - return hr; -} diff --git a/src/dutil/xmlutil.cpp b/src/dutil/xmlutil.cpp deleted file mode 100644 index 0f1e611d..00000000 --- a/src/dutil/xmlutil.cpp +++ /dev/null @@ -1,1332 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define XmlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) -#define XmlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) -#define XmlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) -#define XmlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) -#define XmlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_XMLUTIL, e, x, s, __VA_ARGS__) -#define XmlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_XMLUTIL, g, x, s, __VA_ARGS__) - -// intialization globals -CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} }; -static volatile LONG vcXmlInitialized = 0; -static BOOL vfMsxml40 = FALSE; -static BOOL fComInitialized = FALSE; -BOOL vfMsxml30 = FALSE; - -/******************************************************************** - XmlInitialize - finds an appropriate version of the XML DOM - -*********************************************************************/ -extern "C" HRESULT DAPI XmlInitialize( - ) -{ - HRESULT hr = S_OK; - - if (!fComInitialized) - { - hr = ::CoInitialize(0); - if (RPC_E_CHANGED_MODE != hr) - { - XmlExitOnFailure(hr, "failed to initialize COM"); - fComInitialized = TRUE; - } - } - - LONG cInitialized = ::InterlockedIncrement(&vcXmlInitialized); - if (1 == cInitialized) - { - // NOTE: 4.0 behaves differently than 3.0 so there may be problems doing this -#if 0 - hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument.4.0", &vclsidXMLDOM); - if (S_OK == hr) - { - vfMsxml40 = TRUE; - Trace(REPORT_VERBOSE, "found Msxml2.DOMDocument.4.0"); - ExitFunction(); - } -#endif - hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument", &vclsidXMLDOM); - if (FAILED(hr)) - { - // try to fall back to old MSXML - hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM); - } - XmlExitOnFailure(hr, "failed to get CLSID for XML DOM"); - - Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument26) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument40) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument50) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument60)); - } - - hr = S_OK; -LExit: - return hr; -} - - -/******************************************************************** - XmUninitialize - - -*********************************************************************/ -extern "C" void DAPI XmlUninitialize( - ) -{ - AssertSz(vcXmlInitialized, "XmlUninitialize called when not initialized"); - - LONG cInitialized = ::InterlockedDecrement(&vcXmlInitialized); - - if (0 == cInitialized) - { - memset(&vclsidXMLDOM, 0, sizeof(vclsidXMLDOM)); - - if (fComInitialized) - { - ::CoUninitialize(); - } - } -} - -extern "C" HRESULT DAPI XmlCreateElement( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzElementName, - __out IXMLDOMElement **ppixnElement - ) -{ - if (!ppixnElement || !pixdDocument) - { - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - BSTR bstrElementName = ::SysAllocString(wzElementName); - XmlExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - hr = pixdDocument->createElement(bstrElementName, ppixnElement); -LExit: - ReleaseBSTR(bstrElementName); - return hr; -} - - -/******************************************************************** - XmlCreateDocument - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlCreateDocument( - __in_opt LPCWSTR pwzElementName, - __out IXMLDOMDocument** ppixdDocument, - __out_opt IXMLDOMElement** ppixeRootElement - ) -{ - HRESULT hr = S_OK; - BOOL (WINAPI *pfnDisableWow64)(__out PVOID* ) = NULL; - BOOLEAN (WINAPI *pfnEnableWow64)(__in BOOLEAN ) = NULL; - BOOL (WINAPI *pfnRevertWow64)(__in PVOID ) = NULL; - BOOL fWow64Available = FALSE; - void *pvWow64State = NULL; - - // RELEASEME - IXMLDOMElement* pixeRootElement = NULL; - IXMLDOMDocument *pixdDocument = NULL; - - // Test if we have access to the Wow64 API, and store the result in fWow64Available - HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll"); - XmlExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll"); - - // This will test if we have access to the Wow64 API - if (NULL != GetProcAddress(hKernel32, "IsWow64Process")) - { - pfnDisableWow64 = (BOOL (WINAPI *)(PVOID *))::GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection"); - pfnEnableWow64 = (BOOLEAN (WINAPI *)(BOOLEAN))::GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection"); - pfnRevertWow64 = (BOOL (WINAPI *)(PVOID))::GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection"); - - fWow64Available = pfnDisableWow64 && pfnEnableWow64 && pfnRevertWow64; - } - - // create the top level XML document - AssertSz(vcXmlInitialized, "XmlInitialize() was not called"); - - // Enable Wow64 Redirection, if possible - if (fWow64Available) - { - // 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) - pfnDisableWow64(&pvWow64State); - // If we fail to enable it, don't bother trying to disable it later on - fWow64Available = pfnEnableWow64(TRUE); - } - - hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument); - XmlExitOnFailure(hr, "failed to create XML DOM Document"); - Assert(pixdDocument); - - if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20)) - { - vfMsxml30 = TRUE; - } - - if (pwzElementName) - { - hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement); - XmlExitOnFailure(hr, "failed XmlCreateElement"); - hr = pixdDocument->appendChild(pixeRootElement, NULL); - XmlExitOnFailure(hr, "failed appendChild"); - } - - *ppixdDocument = pixdDocument; - pixdDocument = NULL; - - if (ppixeRootElement) - { - *ppixeRootElement = pixeRootElement; - pixeRootElement = NULL; - } - -LExit: - // Re-disable Wow64 Redirection, if appropriate - if (fWow64Available && !pfnRevertWow64(pvWow64State)) - { - // If we expected to be able to revert, and couldn't, fail in the only graceful way we can - ::ExitProcess(1); - } - - ReleaseObject(pixeRootElement); - ReleaseObject(pixdDocument); - return hr; -} - - -/******************************************************************** - XmlLoadDocument - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocument( - __in_z LPCWSTR wzDocument, - __out IXMLDOMDocument** ppixdDocument - ) -{ - return XmlLoadDocumentEx(wzDocument, 0, ppixdDocument); -} - - -/******************************************************************** - XmlReportParseError - - -*********************************************************************/ -static void XmlReportParseError( - __in IXMLDOMParseError* pixpe - ) -{ - HRESULT hr = S_OK; - long lNumber = 0; - BSTR bstr = NULL; - - Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:"); - - hr = pixpe->get_errorCode(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode."); - Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber); - - hr = pixpe->get_filepos(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos."); - Trace(REPORT_STANDARD, "filepos = %d", lNumber); - - hr = pixpe->get_line(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.line."); - Trace(REPORT_STANDARD, "line = %d", lNumber); - - hr = pixpe->get_linepos(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos."); - Trace(REPORT_STANDARD, "linepos = %d", lNumber); - - hr = pixpe->get_reason(&bstr); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason."); - Trace(REPORT_STANDARD, "reason = %ls", bstr); - ReleaseNullBSTR(bstr); - - hr = pixpe->get_srcText (&bstr); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText ."); - Trace(REPORT_STANDARD, "srcText = %ls", bstr); - ReleaseNullBSTR(bstr); - -LExit: - ReleaseBSTR(bstr); -} - -/******************************************************************** - XmlLoadDocumentEx - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentEx( - __in_z LPCWSTR wzDocument, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ) -{ - HRESULT hr = S_OK; - VARIANT_BOOL vbSuccess = 0; - - // RELEASEME - IXMLDOMDocument* pixd = NULL; - IXMLDOMParseError* pixpe = NULL; - BSTR bstrLoad = NULL; - - if (!wzDocument || !*wzDocument) - { - hr = E_UNEXPECTED; - XmlExitOnFailure(hr, "string must be non-null"); - } - - hr = XmlCreateDocument(NULL, &pixd); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed XmlCreateDocument"); - - if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) - { - hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); - XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); - } - - // Security issue. Avoid triggering anything external. - hr = pixd->put_validateOnParse(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_validateOnParse"); - hr = pixd->put_resolveExternals(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_resolveExternals"); - - bstrLoad = ::SysAllocString(wzDocument); - XmlExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx"); - - hr = pixd->loadXML(bstrLoad, &vbSuccess); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); - } - - if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) - { - XmlReportParseError(pixpe); - } - - XmlExitOnFailure(hr, "failed loadXML"); - - - hr = S_OK; -LExit: - if (ppixdDocument) - { - *ppixdDocument = pixd; - pixd = NULL; - } - ReleaseBSTR(bstrLoad); - ReleaseObject(pixd); - ReleaseObject(pixpe); - - return hr; -} - - -/******************************************************************* - XmlLoadDocumentFromFile - -********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentFromFile( - __in_z LPCWSTR wzPath, - __out IXMLDOMDocument** ppixdDocument - ) -{ - return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument); -} - - -/******************************************************************* - XmlLoadDocumentFromFileEx - -********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx( - __in_z LPCWSTR wzPath, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ) -{ - HRESULT hr = S_OK; - VARIANT varPath; - VARIANT_BOOL vbSuccess = 0; - - IXMLDOMDocument* pixd = NULL; - IXMLDOMParseError* pixpe = NULL; - - ::VariantInit(&varPath); - varPath.vt = VT_BSTR; - varPath.bstrVal = ::SysAllocString(wzPath); - XmlExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx"); - - hr = XmlCreateDocument(NULL, &pixd); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed XmlCreateDocument"); - - if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) - { - hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); - XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); - } - - // Avoid triggering anything external. - hr = pixd->put_validateOnParse(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_validateOnParse"); - hr = pixd->put_resolveExternals(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_resolveExternals"); - - pixd->put_async(VARIANT_FALSE); - hr = pixd->load(varPath, &vbSuccess); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); - } - - if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) - { - XmlReportParseError(pixpe); - } - - XmlExitOnFailure(hr, "failed to load XML from: %ls", wzPath); - - if (ppixdDocument) - { - *ppixdDocument = pixd; - pixd = NULL; - } - - hr = S_OK; -LExit: - ReleaseVariant(varPath); - ReleaseObject(pixd); - ReleaseObject(pixpe); - - return hr; -} - - -/******************************************************************** - XmlLoadDocumentFromBuffer - -*********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( - __in_bcount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out IXMLDOMDocument** ppixdDocument - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixdDocument = NULL; - SAFEARRAY sa = { }; - VARIANT vtXmlSource; - VARIANT_BOOL vbSuccess = 0; - - ::VariantInit(&vtXmlSource); - - // create document - hr = XmlCreateDocument(NULL, &pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed XmlCreateDocument"); - - // Security issue. Avoid triggering anything external. - hr = pixdDocument->put_validateOnParse(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_validateOnParse"); - hr = pixdDocument->put_resolveExternals(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_resolveExternals"); - - // load document - sa.cDims = 1; - sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE; - sa.cbElements = 1; - sa.pvData = (PVOID)pbSource; - sa.rgsabound[0].cElements = (ULONG)cbSource; - vtXmlSource.vt = VT_ARRAY | VT_UI1; - vtXmlSource.parray = &sa; - - hr = pixdDocument->load(vtXmlSource, &vbSuccess); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); - } - XmlExitOnFailure(hr, "failed loadXML"); - - // return value - *ppixdDocument = pixdDocument; - pixdDocument = NULL; - -LExit: - ReleaseObject(pixdDocument); - return hr; -} - - -/******************************************************************** - XmlSetAttribute - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in_z LPCWSTR pwzAttributeValue - ) -{ - HRESULT hr = S_OK; - VARIANT varAttributeValue; - ::VariantInit(&varAttributeValue); - - // RELEASEME - IXMLDOMDocument* pixdDocument = NULL; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMAttribute* pixaAttribute = NULL; - IXMLDOMNode* pixaNode = NULL; - BSTR bstrAttributeName = ::SysAllocString(pwzAttribute); - XmlExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute"); - - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute); - - hr = pixnNode->get_ownerDocument(&pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); - - hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute); - XmlExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute); - - varAttributeValue.vt = VT_BSTR; - varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue); - if (!varAttributeValue.bstrVal) - { - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - } - XmlExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute"); - - hr = pixaAttribute->put_nodeValue(varAttributeValue); - XmlExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute); - - hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode); - XmlExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute); - -LExit: - ReleaseObject(pixdDocument); - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixaAttribute); - ReleaseObject(pixaNode); - ReleaseBSTR(varAttributeValue.bstrVal); - ReleaseBSTR(bstrAttributeName); - - return hr; -} - - -/******************************************************************** - XmlSelectSingleNode - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSelectSingleNode( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNode **ppixnChild - ) -{ - HRESULT hr = S_OK; - - BSTR bstrXPath = NULL; - - XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode"); - XmlExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode"); - - bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); - XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode"); - - hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild); - -LExit: - ReleaseBSTR(bstrXPath); - - return hr; -} - - -/******************************************************************** - XmlCreateTextNode - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlCreateTextNode( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzText, - __out IXMLDOMText **ppixnTextNode - ) -{ - if (!ppixnTextNode || !pixdDocument) - { - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - BSTR bstrText = ::SysAllocString(wzText); - XmlExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString"); - hr = pixdDocument->createTextNode(bstrText, ppixnTextNode); -LExit: - ReleaseBSTR(bstrText); - - return hr; -} - - -/******************************************************************** - XmlGetText - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetText( - __in IXMLDOMNode* pixnNode, - __deref_out_z BSTR* pbstrText - ) -{ - return pixnNode->get_text(pbstrText); -} - - -/******************************************************************** - XmlGetAttribute - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __deref_out_z BSTR* pbstrAttributeValue - ) -{ - Assert(pixnNode); - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - VARIANT varAttributeValue; - BSTR bstrAttribute = SysAllocString(pwzAttribute); - - // INIT - ::VariantInit(&varAttributeValue); - - // get attribute value from source - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "failed get_attributes"); - - hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); - if (S_FALSE == hr) - { - // hr = E_FAIL; - ExitFunction(); - } - XmlExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute); - - hr = pixnAttribute->get_nodeValue(&varAttributeValue); - XmlExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute); - - // steal the BSTR from the VARIANT - if (S_OK == hr && pbstrAttributeValue) - { - *pbstrAttributeValue = varAttributeValue.bstrVal; - varAttributeValue.bstrVal = NULL; - } - -LExit: - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - ReleaseVariant(varAttributeValue); - ReleaseBSTR(bstrAttribute); - - return hr; -} - - -/******************************************************************** - XmlGetAttributeEx - -*********************************************************************/ -HRESULT DAPI XmlGetAttributeEx( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __deref_out_z LPWSTR* psczAttributeValue - ) -{ - Assert(pixnNode); - HRESULT hr = S_OK; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - VARIANT varAttributeValue; - BSTR bstrAttribute = NULL; - - ::VariantInit(&varAttributeValue); - - // get attribute value from source - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "Failed get_attributes."); - - bstrAttribute = ::SysAllocString(wzAttribute); - XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR."); - - hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); - if (S_FALSE == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - XmlExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute); - - hr = pixnAttribute->get_nodeValue(&varAttributeValue); - if (S_FALSE == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - XmlExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute); - - // copy value - hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0); - XmlExitOnFailure(hr, "Failed to copy attribute value."); - -LExit: - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - ReleaseVariant(varAttributeValue); - ReleaseBSTR(bstrAttribute); - - return hr; -} - - -/******************************************************************** - XmlGetYesNoAttribute - -*********************************************************************/ -HRESULT DAPI XmlGetYesNoAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __out BOOL* pfYes - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue); - if (E_NOTFOUND != hr) - { - XmlExitOnFailure(hr, "Failed to get attribute."); - - *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1); - } - -LExit: - ReleaseStr(sczValue); - - return hr; -} - - - -/******************************************************************** - XmlGetAttributeNumber - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttributeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD* pdwValue - ) -{ - HRESULT hr = XmlGetAttributeNumberBase(pixnNode, pwzAttribute, 10, pdwValue); - return hr; -} - - -/******************************************************************** - XmlGetAttributeNumberBase - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttributeNumberBase( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in int nBase, - __out DWORD* pdwValue - ) -{ - HRESULT hr = S_OK; - BSTR bstrPointer = NULL; - - hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer); - XmlExitOnFailure(hr, "Failed to get value from attribute."); - - if (S_OK == hr) - { - *pdwValue = wcstoul(bstrPointer, NULL, nBase); - } - -LExit: - ReleaseBSTR(bstrPointer); - return hr; -} - - -/******************************************************************** - XmlGetAttributeLargeNumber - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttributeLargeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD64* pdw64Value - ) -{ - HRESULT hr = S_OK; - BSTR bstrValue = NULL; - - hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue); - XmlExitOnFailure(hr, "failed XmlGetAttribute"); - - if (S_OK == hr) - { - LONGLONG ll = 0; - hr = StrStringToInt64(bstrValue, 0, &ll); - XmlExitOnFailure(hr, "Failed to treat attribute value as number."); - - *pdw64Value = ll; - } - else - { - *pdw64Value = 0; - } - -LExit: - ReleaseBSTR(bstrValue); - return hr; -} - - -/******************************************************************** - XmlGetNamedItem - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetNamedItem( - __in IXMLDOMNamedNodeMap *pixnmAttributes, - __in_opt LPCWSTR wzName, - __out IXMLDOMNode **ppixnNamedItem - ) -{ - if (!pixnmAttributes || !ppixnNamedItem) - { - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - BSTR bstrName = ::SysAllocString(wzName); - XmlExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem); - -LExit: - ReleaseBSTR(bstrName); - return hr; -} - - -/******************************************************************** - XmlSetText - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSetText( - __in IXMLDOMNode *pixnNode, - __in_z LPCWSTR pwzText - ) -{ - Assert(pixnNode && pwzText); - HRESULT hr = S_OK; - DOMNodeType dnType; - - // RELEASEME - IXMLDOMDocument* pixdDocument = NULL; - IXMLDOMNodeList* pixnlNodeList = NULL; - IXMLDOMNode* pixnChildNode = NULL; - IXMLDOMText* pixtTextNode = NULL; - VARIANT varText; - - ::VariantInit(&varText); - - // find the text node - hr = pixnNode->get_childNodes(&pixnlNodeList); - XmlExitOnFailure(hr, "failed to get child nodes"); - - while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode))) - { - hr = pixnChildNode->get_nodeType(&dnType); - XmlExitOnFailure(hr, "failed to get node type"); - - if (NODE_TEXT == dnType) - break; - ReleaseNullObject(pixnChildNode); - } - if (S_FALSE == hr) - { - hr = S_OK; - } - - if (pixnChildNode) - { - varText.vt = VT_BSTR; - varText.bstrVal = ::SysAllocString(pwzText); - if (!varText.bstrVal) - { - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - } - XmlExitOnFailure(hr, "failed SysAllocString in XmlSetText"); - - hr = pixnChildNode->put_nodeValue(varText); - XmlExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue"); - } - else - { - hr = pixnNode->get_ownerDocument(&pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); - - hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode); - XmlExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText); - - hr = pixnNode->appendChild(pixtTextNode, NULL); - XmlExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText); - } - - hr = *pwzText ? S_OK : S_FALSE; - -LExit: - ReleaseObject(pixnlNodeList); - ReleaseObject(pixnChildNode); - ReleaseObject(pixdDocument); - ReleaseObject(pixtTextNode); - ReleaseVariant(varText); - return hr; -} - - -/******************************************************************** - XmlSetTextNumber - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSetTextNumber( - __in IXMLDOMNode *pixnNode, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - WCHAR wzValue[12]; - - hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue); - XmlExitOnFailure(hr, "Failed to format numeric value as string."); - - hr = XmlSetText(pixnNode, wzValue); - -LExit: - return hr; -} - - -/******************************************************************** - XmlCreateChild - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlCreateChild( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR pwzElementType, - __out IXMLDOMNode** ppixnChild - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMDocument* pixdDocument = NULL; - IXMLDOMNode* pixnChild = NULL; - - hr = pixnParent->get_ownerDocument(&pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed get_ownerDocument"); - - hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed createElement"); - - pixnParent->appendChild(pixnChild,NULL); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed appendChild"); - - if (ppixnChild) - { - *ppixnChild = pixnChild; - pixnChild = NULL; - } - -LExit: - ReleaseObject(pixdDocument); - ReleaseObject(pixnChild); - return hr; -} - -/******************************************************************** - XmlRemoveAttribute - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlRemoveAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - BSTR bstrAttribute = ::SysAllocString(pwzAttribute); - XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute"); - - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute); - - hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL); - XmlExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute); - -LExit: - ReleaseObject(pixnnmAttributes); - ReleaseBSTR(bstrAttribute); - - return hr; -} - - -/******************************************************************** - XmlSelectNodes - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSelectNodes( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNodeList **ppixnlChildren - ) -{ - HRESULT hr = S_OK; - - BSTR bstrXPath = NULL; - - XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes"); - XmlExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes"); - - bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); - XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes"); - - hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren); - -LExit: - ReleaseBSTR(bstrXPath); - return hr; -} - - -/******************************************************************** - XmlNextAttribute - returns the next attribute in a node list - - NOTE: pbstrAttribute is optional - returns S_OK if found an element - returns S_FALSE if no element found - returns E_* if something went wrong -********************************************************************/ -extern "C" HRESULT DAPI XmlNextAttribute( - __in IXMLDOMNamedNodeMap* pixnnm, - __out IXMLDOMNode** pixnAttribute, - __deref_opt_out_z_opt BSTR* pbstrAttribute - ) -{ - Assert(pixnnm && pixnAttribute); - - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - DOMNodeType nt; - - // null out the return values - *pixnAttribute = NULL; - if (pbstrAttribute) - { - *pbstrAttribute = NULL; - } - - hr = pixnnm->nextNode(&pixn); - XmlExitOnFailure(hr, "Failed to get next attribute."); - - if (S_OK == hr) - { - hr = pixn->get_nodeType(&nt); - XmlExitOnFailure(hr, "failed to get node type"); - - if (NODE_ATTRIBUTE != nt) - { - hr = E_UNEXPECTED; - XmlExitOnFailure(hr, "Failed to get expected node type back: attribute"); - } - - // if the caller asked for the attribute name - if (pbstrAttribute) - { - hr = pixn->get_baseName(pbstrAttribute); - XmlExitOnFailure(hr, "failed to get attribute name"); - } - - *pixnAttribute = pixn; - pixn = NULL; - } - -LExit: - ReleaseObject(pixn); - return hr; -} - - -/******************************************************************** - XmlNextElement - returns the next element in a node list - - NOTE: pbstrElement is optional - returns S_OK if found an element - returns S_FALSE if no element found - returns E_* if something went wrong -********************************************************************/ -extern "C" HRESULT DAPI XmlNextElement( - __in IXMLDOMNodeList* pixnl, - __out IXMLDOMNode** pixnElement, - __deref_opt_out_z_opt BSTR* pbstrElement - ) -{ - Assert(pixnl && pixnElement); - - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - DOMNodeType nt; - - // null out the return values - *pixnElement = NULL; - if (pbstrElement) - { - *pbstrElement = NULL; - } - - // - // find the next element in the list - // - while (S_OK == (hr = pixnl->nextNode(&pixn))) - { - hr = pixn->get_nodeType(&nt); - XmlExitOnFailure(hr, "failed to get node type"); - - if (NODE_ELEMENT == nt) - break; - - ReleaseNullObject(pixn); - } - XmlExitOnFailure(hr, "failed to get next element"); - - // if we have a node and the caller asked for the element name - if (pixn && pbstrElement) - { - hr = pixn->get_baseName(pbstrElement); - XmlExitOnFailure(hr, "failed to get element name"); - } - - *pixnElement = pixn; - pixn = NULL; - - hr = *pixnElement ? S_OK : S_FALSE; -LExit: - ReleaseObject(pixn); - return hr; -} - - -/******************************************************************** - XmlRemoveChildren - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlRemoveChildren( - __in IXMLDOMNode* pixnSource, - __in_z LPCWSTR pwzXPath - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMNodeList* pixnlNodeList = NULL; - IXMLDOMNode* pixnNode = NULL; - IXMLDOMNode* pixnRemoveChild = NULL; - - if (pwzXPath) - { - hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList); - XmlExitOnFailure(hr, "failed XmlSelectNodes"); - } - else - { - hr = pixnSource->get_childNodes(&pixnlNodeList); - XmlExitOnFailure(hr, "failed childNodes"); - } - if (S_FALSE == hr) - { - ExitFunction(); - } - - while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode))) - { - hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild); - XmlExitOnFailure(hr, "failed removeChild"); - - ReleaseNullObject(pixnRemoveChild); - ReleaseNullObject(pixnNode); - } - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseObject(pixnlNodeList); - ReleaseObject(pixnNode); - ReleaseObject(pixnRemoveChild); - - return hr; -} - - -/******************************************************************** - XmlSaveDocument - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSaveDocument( - __in IXMLDOMDocument* pixdDocument, - __inout LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - VARIANT varsDestPath; - - ::VariantInit(&varsDestPath); - varsDestPath.vt = VT_BSTR; - varsDestPath.bstrVal = ::SysAllocString(wzPath); - if (!varsDestPath.bstrVal) - { - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - } - XmlExitOnFailure(hr, "failed to create BSTR"); - - hr = pixdDocument->save(varsDestPath); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed save in WriteDocument"); - -LExit: - ReleaseVariant(varsDestPath); - return hr; -} - - -/******************************************************************** - XmlSaveDocumentToBuffer - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSaveDocumentToBuffer( - __in IXMLDOMDocument* pixdDocument, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out DWORD* pcbDest - ) -{ - HRESULT hr = S_OK; - IStream* pStream = NULL; - LARGE_INTEGER li = { }; - STATSTG statstg = { }; - BYTE* pbDest = NULL; - ULONG cbRead = 0; - VARIANT vtDestination; - - ::VariantInit(&vtDestination); - - // create stream - hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream); - XmlExitOnFailure(hr, "Failed to create stream."); - - // write document to stream - vtDestination.vt = VT_UNKNOWN; - vtDestination.punkVal = (IUnknown*)pStream; - hr = pixdDocument->save(vtDestination); - XmlExitOnFailure(hr, "Failed to save document."); - - // get stream size - hr = pStream->Stat(&statstg, STATFLAG_NONAME); - XmlExitOnFailure(hr, "Failed to get stream size."); - - // allocate buffer - pbDest = static_cast(MemAlloc(statstg.cbSize.LowPart, TRUE)); - XmlExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); - - // read data from stream - li.QuadPart = 0; - hr = pStream->Seek(li, STREAM_SEEK_SET, NULL); - XmlExitOnFailure(hr, "Failed to seek stream."); - - hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead); - if (cbRead < statstg.cbSize.LowPart) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "Failed to read stream content to buffer."); - - // return value - *ppbDest = pbDest; - pbDest = NULL; - *pcbDest = statstg.cbSize.LowPart; - -LExit: - ReleaseObject(pStream); - ReleaseMem(pbDest); - return hr; -} diff --git a/src/dutil/xsd/thmutil.xsd b/src/dutil/xsd/thmutil.xsd deleted file mode 100644 index 46c20e4a..00000000 --- a/src/dutil/xsd/thmutil.xsd +++ /dev/null @@ -1,1188 +0,0 @@ - - - - - - - - Schema for describing Theme files processed by thmutil. - - - - - - - - - This is the top-level container element for every thmutil Theme file. - - - - - - - - - - - Relative path to an image file that can serve as a single source for images in the rest of the theme. - This image is referenced by controls using the SourceX and SourceY attributes. - Mutually exclusive with the ImageResource attribute. - - - - - - - Identifier that references an image resource in the module for the window. - Mutually exclusive with the ImageFile attribute. - - - - - - - - - Defines a font including the size and color. - - - - - - Name of the font face (required). - - - - 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. - - - - - Font size. Use negative numbers to specify the font in pixels. - - - - - Font weight. - - - - - - A system color id or a hexadecimal value representing BGR foreground color of the font. - "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. - If this attribute is absent the foreground will be transparent. - Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. - - - - - - - A system color id or a hexadecimal value representing BGR background color of the font. - "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. - If this attribute is absent the background will be transparent. - Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. - - - - - - Specifies whether the font is underlined. - - - - - - - - - - List of images which can be shared between multiple controls. - - - - - - - - - Name of the ImageList, to be referenced by other controls. - - - - - - - - - Named set of controls that can be shown and hidden collectively. - - - - - - - Optional name for the page. - - - - - - - - - Defines the overall look of the main window. - - - - - - - - - - Specifies whether the ThmUtil default window proc should process WM_SIZE and WM_SIZING events. - - - - - - Caption for the window. - This is required if not using the StringId attribute. - - - - - - Numeric identifier to the Font element that serves as the default font for the window. - - - - - Height of the window's client area. - - - - - - Hexadecimal window style. If this is not specified the default value is: WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU. - If SourceX and SourceY are specified, then WS_OVERLAPPED is replaced with WS_POPUP. - - - - - - Relative path to an icon file for the window. Mutually exclusive with IconResource and SourceX and SourceY attributes. - - - - - - Identifier that references an icon resource in the module for the icon for the window. - Mutually exclusive with IconFile and SourceX and SourceY attributes. - - - - - - Minimum height of the window. Only functions if AutoResize is enabled. - - - - - Minimum width of the window. Only functions if AutoResize is enabled. - - - - - X offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. - - - - - Y offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. - - - - - - Identifier that references a string resource in the module to define the window caption. - Mutually exclusive with the Caption attribute. - - - - - - Width of the window's client area. - - - - - - - - Defines a control that rotates through a set of images on a specified interval. - - - - - - - - - - Specifies the time to wait before showing the next image, in milliseconds. - - - - - - Specifies whether the billboard should loop through the images infinitely. - - - - - - - - Defines a button. - - - - - Text to display in the button. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - 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). - If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. - - - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. - - - - - Numeric identifier to the Font element that serves as the font when the control is hovered over. Only valid when using graphic buttons. - - - - - - Relative path to an image file to define a graphic button. - The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. - Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - - - Identifier that references an image resource in the module to define a graphic button. - The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. - Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - Numeric identifier to the Font element that serves as the font when the control is selected. Only valid when using graphic buttons. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - - When the button is pressed, a directory browser dialog is shown. - - - - - - - The condition that determines if the parent control will execute this action. - - - - - - - The name of the variable to update when the user selects a directory from the dialog. - - - - - - - - - - When the button is pressed, the specified page is shown. - - - - - - - When set to 'yes', none of the variable changes made on the current page are saved. - - - - - - - The condition that determines if the parent control will execute this action. - - - - - - - The Name of the Page to show. - - - - - - - - - - When the button is pressed, the WM_CLOSE message is sent to the window. - - - - - - - The condition that determines if the parent control will execute this action. - - - - - - - - - Defines a checkbox. - - - - - Text to display beside the checkbox. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines a combobox. - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - - - Defines a button. - - - - - Text to display in the button. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - 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). - If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. - - - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. - - - - - - Relative path to an icon file to define a command link glyph. - Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - - - Identifier that references an icon resource in the module to define a command link glyph. - Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - - Relative path to an image file to define a command link glyph. - Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - - - Identifier that references an image resource in the module to define a command link glyph. - Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines an edit box. - - - - - - - Initial text for the control. - Mutually exclusive with the StringId attribute. - - - - - - Specifies whether the edit box should auto-complete with file system paths. - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the initial text for the control. - - - - - - - - - - - Defines a hyperlink. - - - - - Text to display as the link. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the unselected font. - - - - - Numeric identifier to the Font element that serves as the font when the control is hovered over. - - - - - Numeric identifier to the Font element that serves as the font when the control is selected. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines a text block with support for HTML <a> tags. - - - - - Text to display as the link. - Use HTML <a href="URL"> to create a link. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines an image for an ImageList or Billboard. - - - - - Relative path to an image file. Mutually exclusive with ImageResource. - - - - - Identifier that references an image resource in the module. Mutually exclusive with ImageFile. - - - - - - - - Defines an image. - - - - - - Relative path to an image file. Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - Identifier that references an image resource in the module. Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - - - Defines a label. - - - - - Text for the label to display. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Specifies whether the text should be centered horizontally in the width of the control. Default is "no". - - - - - By default ampersands (&) 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". - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the label. - - - - - - - - - Defines a listview. - - - - - - - - - Numeric identifier to the Font element that serves as the default font for the ListView. - - - - - Hexadecimal extended window style. - - - - - - The name of the ImageList to assign to this listview with type LVSIL_NORMAL. - - - - - - - The name of the ImageList to assign to this listview with type LVSIL_SMALL. - - - - - - - The name of the ImageList to assign to this listview with type LVSIL_STATE. - - - - - - - The name of the ImageList to assign to this listview with type LVSIL_GROUPHEADER. - - - - - - - - - - Defines note text for a command link control based on an optional condition. - 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). - 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. - - - - - - - - Note text for the parent command link control. - - - - - - The condition that determines when the parent control will use this note text. - - - - - - - - - - - Defines a collection of controls. - - - - - - - - - - Defines a progress bar. - - - - - - 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. - - - - - 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. - - - - - - - - Defines an individual radio button within a set of radio buttons. - - - - - Text to display beside the radio button. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - Optional value used when setting the variable associated with the set of radio buttons. - - - - - - - - Defines a set of radio buttons. - - - - - - - - Optional variable name for the set of radio buttons. - - - - - - - - Defines a rich edit control. - - - - - Initial text for the control. - Mutually exclusive with the StringId attribute. - - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - - Identifier that references a string resource in the module to define the initial text for the control. - - - - - - - - - Defines a straight line. - - - - - - - - - Defines an individual tab within a set of tabs. - - - - - - - Caption of the tab. - Mutually exclusive with the StringId attribute. - - - - - - Identifier that references a string resource in the module to define the caption of the tab. - - - - - - - - - - - Defines a set of tabs. - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - - - - Defines text for the parent control based on an optional condition. - 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). - 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. - - - - - - - - Text for the parent control. - - - - - - The condition that determines when the parent control will use this text. - - - - - - - - - - - - Defines text for the parent control's tooltip. - - - - - - - - Text for the parent control's tooltip. - - - - - - - - - - Defines a treeview. - - - - - - Specifies whether the row always appears selected even when the treeview has lost focus. - - - - - Specifies whether drag and drop is enabled for the treeview. - - - - - Specifies whether an entire row is selected for the treeview. - - - - - Specifies whether the treeview will show buttons. - - - - - Specifies whether lines appear for all treeview items. - - - - - Specifies whether the root nodes have lines beside them. - - - - - - - - A column of a list. - - - - - - - Text for the column header. - Mutually exclusive with the StringId attribute. - - - - - Width of the column. - - - - - - Whether or not this column can grow to fill available width of the listview. - More than one column can be marked with yes - all expandable columns will share available extra space. - This is especially useful if the Window/@AutoResize is yes. - - - - - - - Identifier that references a string resource in the module to define the text for the column header. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Optional name for the control. - - - - - Set to 'yes' to disable automatic variable getting and setting, EnableCondition, VisibleCondition, and conditional Text elements. The default is 'no'. - - - - - A condition that determines if the control is enabled. If this condition is true or omitted, then the control will be enabled. - - - - - Height of the control. Non-positive values extend the control to the bottom of the window minus the value. - - - - - Hexadecimal window style for the control. - - - - - Specifies whether the control should be hidden when disabled. - - - - - Specifies whether the control is part of the tab sequence of controls. - - - - - Specifies whether the control is initially visible. - - - - - - A condition that determines if the control is visible. If this condition is true or omitted, then the control will be visible. - - - - - - Width of the control. Non-positive values extend the control to the right of the window minus the value. - - - - - 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. - - - - - 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. - - - - - - - Values of this type will either be "yes" or "no". - - - - - - - - - - - Indicates a system color for a font. - - - - - - - - - - - - - - - - - - Indicates the foreground or background color of a font. - - - - - 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 @@ + + + + + + + + \ 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 @@ + + + + + + Debug + false + + $(MSBuildProjectName) + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL + WiX Toolset + + + + + 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 @@ + + + + + + $(BaseOutputPath)obj\.tools + $(SigningToolFolder)\SignClient.exe + $(SigningToolFolder)\empty-filelist.txt + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json + + + + false + $(OutputPath)\$(AssemblyName).xml + + + + + $(PrivateRepositoryUrl.Replace('.git','')) + + $(MSBuildProjectName).nuspec + $(MSBuildProjectDirectory) + $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" + $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) + true + snupkg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + true + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + false + + 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 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + $(Company) + $(Copyright) + + win-x86;win-x64;win-arm64 + native,Version=v0.0 + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + 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 @@ + + + + + + + $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ + $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ + + 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 @@ +# dutil +dutil.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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) +#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +AclCalculateServiceSidString - gets the SID string for the given service name + +NOTE: psczSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclCalculateServiceSidString( + __in LPCWSTR wzServiceName, + __in SIZE_T cchServiceName, + __deref_out_z LPWSTR* psczSid + ) +{ + // TODO: use undocumented RtlCreateServiceSid function? + // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx + // Assume little endian. + HRESULT hr = S_OK; + LPWSTR sczUpperServiceName = NULL; + DWORD cbHash = SHA1_HASH_LEN; + BYTE* pbHash = NULL; + + Assert(psczSid); + + if (0 == cchServiceName) + { + hr = ::StringCchLengthW(wzServiceName, STRSAFE_MAX_CCH, reinterpret_cast(&cchServiceName)); + AclExitOnFailure(hr, "Failed to get the length of the service name."); + } + + hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName); + AclExitOnFailure(hr, "Failed to upper case the service name."); + + pbHash = reinterpret_cast(MemAlloc(cbHash, TRUE)); + AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); + + hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * sizeof(WCHAR), PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); + AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); + + hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u", + MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])), + MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])), + MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])), + MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])), + MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19]))); + +LExit: + ReleaseMem(pbHash); + ReleaseStr(sczUpperServiceName); + + return hr; +} + + +/******************************************************************** +AclGetAccountSidStringEx - gets a string version of the account's SID + calculates a service's SID if lookup fails + +NOTE: psczSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSidStringEx( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* psczSid + ) +{ + HRESULT hr = S_OK; + SIZE_T cchAccount = 0; + PSID psid = NULL; + LPWSTR pwz = NULL; + LPWSTR sczSid = NULL; + + Assert(psczSid); + + hr = AclGetAccountSid(wzSystem, wzAccount, &psid); + if (SUCCEEDED(hr)) + { + Assert(::IsValidSid(psid)); + + if (!::ConvertSidToStringSidW(psid, &pwz)) + { + AclExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount); + } + + hr = StrAllocString(psczSid, pwz, 0); + } + else + { + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) + { + HRESULT hrLength = ::StringCchLengthW(wzAccount, STRSAFE_MAX_CCH, reinterpret_cast(&cchAccount)); + AclExitOnFailure(hrLength, "Failed to get the length of the account name."); + + if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11)) + { + // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it. + LPCWSTR wzServiceName = &wzAccount[11]; + hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid); + AclExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName); + + *psczSid = sczSid; + sczSid = NULL; + } + } + AclExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount); + } + +LExit: + ReleaseStr(sczSid); + if (pwz) + { + ::LocalFree(pwz); + } + if (psid) + { + AclFreeSid(psid); + } + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) +#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +AclCheckAccess - determines if token has appropriate privileges + +NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero +if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckAccess( + __in HANDLE hToken, + __in ACL_ACCESS* paa + ) +{ + HRESULT hr = S_OK; + PSID psid = NULL; + BOOL fIsMember = FALSE; + + AclExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check"); + Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask); + + if (paa->pwzAccountName) + { + hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid); + AclExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName); + } + else + { + 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)) + { + AclExitWithLastError(hr, "failed to initialize SID"); + } + } + + if (!::CheckTokenMembership(hToken, psid, &fIsMember)) + { + AclExitWithLastError(hr, "failed to check membership"); + } + + fIsMember ? hr = S_OK : hr = S_FALSE; + +LExit: + if (psid) + { + ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid? + } + + return hr; +} + + +/******************************************************************** +AclCheckAdministratorAccess - determines if token has Administrator privileges + +NOTE: if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckAdministratorAccess( + __in HANDLE hToken + ) +{ + ACL_ACCESS aa; + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; + + memset(&aa, 0, sizeof(aa)); + aa.sia = siaNt; + aa.nSubAuthorityCount = 2; + aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID; + aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS; + + return AclCheckAccess(hToken, &aa); +} + + +/******************************************************************** +AclCheckLocalSystemAccess - determines if token has LocalSystem privileges + +NOTE: if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckLocalSystemAccess( + __in HANDLE hToken + ) +{ + ACL_ACCESS aa; + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; + + memset(&aa, 0, sizeof(aa)); + aa.sia = siaNt; + aa.nSubAuthorityCount = 1; + aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID; + + return AclCheckAccess(hToken, &aa); +} + + +/******************************************************************** +AclGetWellKnownSid - returns a SID for the specified account + +********************************************************************/ +extern "C" HRESULT DAPI AclGetWellKnownSid( + __in WELL_KNOWN_SID_TYPE wkst, + __deref_out PSID* ppsid + ) +{ + Assert(ppsid); + + HRESULT hr = S_OK;; + PSID psid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + + PSID psidTemp = NULL; +#if(_WIN32_WINNT < 0x0501) + SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; + SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY; + BOOL fSuccess = FALSE; +#endif + + // + // allocate memory for the SID and get it + // + psid = static_cast(MemAlloc(cbSid, TRUE)); + AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID"); + +#if(_WIN32_WINNT < 0x0501) + switch (wkst) + { + case WinWorldSid: // Everyone + fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinAuthenticatedUserSid: // Authenticated Users + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinLocalSystemSid: // LocalSystem + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinLocalServiceSid: // LocalService + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinNetworkServiceSid: // NetworkService + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinGuestsSid: // Guests + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinAdministratorsSid: // Administrators + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinUsersSid: // Users + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinCreatorOwnerSid: //CREATOR OWNER + fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinInteractiveSid: // INTERACTIVE + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + default: + hr = E_INVALIDARG; + AclExitOnFailure(hr, "unknown well known SID: %d", wkst); + } + + if (!fSuccess) + AclExitOnLastError(hr, "failed to allocate well known SID: %d", wkst); + + if (!::CopySid(cbSid, psid, psidTemp)) + AclExitOnLastError(hr, "failed to create well known SID: %d", wkst); +#else + Assert(NULL == psidTemp); + if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid)) + { + AclExitWithLastError(hr, "failed to create well known SID: %d", wkst); + } +#endif + + *ppsid = psid; + psid = NULL; // null it here so it won't be released below + + Assert(S_OK == hr && ::IsValidSid(*ppsid)); +LExit: + if (psidTemp) + { + ::FreeSid(psidTemp); + } + + ReleaseMem(psid); + + return hr; +} + + +/******************************************************************** +AclGetAccountSid - returns a SID for the specified account + +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSid( + __in_opt LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out PSID* ppsid + ) +{ + Assert(wzAccount && *wzAccount && ppsid); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + PSID psid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + LPWSTR pwzDomainName = NULL; + DWORD cbDomainName = 255; + SID_NAME_USE peUse; + + // + // allocate memory for the SID and domain name + // + psid = static_cast(MemAlloc(cbSid, TRUE)); + AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID"); + hr = StrAlloc(&pwzDomainName, cbDomainName); + AclExitOnFailure(hr, "failed to allocate string for domain name"); + + // + // try to lookup the account now + // + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) + { + // if one of the buffers wasn't large enough + er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + if (SECURITY_MAX_SID_SIZE < cbSid) + { + PSID psidNew = static_cast(MemReAlloc(psid, cbSid, TRUE)); + AclExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount); + + psid = psidNew; + } + if (255 < cbDomainName) + { + hr = StrAlloc(&pwzDomainName, cbDomainName); + AclExitOnFailure(hr, "failed to allocate string for domain name"); + } + + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) + { + AclExitWithLastError(hr, "failed to lookup account: %ls", wzAccount); + } + } + else + { + AclExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount); + } + } + + *ppsid = psid; + psid = NULL; + + hr = S_OK; +LExit: + ReleaseStr(pwzDomainName); + ReleaseMem(psid); + + return hr; +} + + +/******************************************************************** +AclGetAccountSidString - gets a string version of the user's SID + +NOTE: ppwzSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSidString( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* ppwzSid + ) +{ + Assert(ppwzSid); + HRESULT hr = S_OK; + PSID psid = NULL; + LPWSTR pwz = NULL; + + *ppwzSid = NULL; + + hr = AclGetAccountSid(wzSystem, wzAccount, &psid); + AclExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount); + Assert(::IsValidSid(psid)); + + if (!::ConvertSidToStringSidW(psid, &pwz)) + { + AclExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount); + } + + hr = StrAllocString(ppwzSid, pwz, 0); + +LExit: + if (FAILED(hr)) + { + ReleaseNullStr(*ppwzSid); + } + + if (pwz) + { + ::LocalFree(pwz); + } + + if (psid) + { + AclFreeSid(psid); + } + + return hr; +} + + +/******************************************************************** +AclCreateDacl - creates a DACL from ACL_ACE structures + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateDacl( + __in_ecount(cDeny) ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount(cAllow) ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAcl + ) +{ + Assert(ppAcl); + HRESULT hr = S_OK; + ACL* pAcl = NULL; + DWORD cbAcl = 0; + DWORD i; + + *ppAcl = NULL; + + // initialize the ACL + cbAcl = sizeof(ACL); + for (i = 0; i < cDeny; ++i) + { + cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD); + } + + for (i = 0; i < cAllow; ++i) + { + cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD); + } + + pAcl = static_cast(MemAlloc(cbAcl, TRUE)); + AclExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to initialize ACL"); + } + + // add in the ACEs (denied first) + for (i = 0; i < cDeny; ++i) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i); + } + } + for (i = 0; i < cAllow; ++i) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access allowed ACE #%d to ACL", i); + } + } + + *ppAcl = pAcl; + pAcl = NULL; + AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL"); + Assert(S_OK == hr); +LExit: + if (pAcl) + { + AclFreeDacl(pAcl); + } + + return hr; +} + + +/******************************************************************** +AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure + +********************************************************************/ +extern "C" HRESULT DAPI AclAddToDacl( + __in ACL* pAcl, + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAclNew + ) +{ + Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew); + HRESULT hr = S_OK; + + ACL_SIZE_INFORMATION asi; + ACL_ACE* paaNewDeny = NULL; + DWORD cNewDeny = 0; + ACL_ACE* paaNewAllow = NULL; + DWORD cNewAllow = 0; + + ACCESS_ALLOWED_ACE* paaa; + ACCESS_DENIED_ACE* pada; + DWORD i; + + // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay) + if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation)) + { + AclExitWithLastError(hr, "failed to get information about original ACL"); + } + + if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow + (asi.AceCount + cDeny) < cDeny || // check for overflow + (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE)) + { + hr = E_OUTOFMEMORY; + AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny)); + } + + paaNewDeny = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE)); + AclExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs"); + + if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow + (asi.AceCount + cAllow) < cAllow || // check for overflow + (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE)) + { + hr = E_OUTOFMEMORY; + AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow)); + } + + paaNewAllow = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE)); + AclExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs"); + + // fill in the new structures with old data then new data (denied first) + for (i = 0; i < asi.AceCount; ++i) + { + if (!::GetAce(pAcl, i, reinterpret_cast(&pada))) + { + AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + } + + if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType) + { + continue; // skip non-denied aces + } + + paaNewDeny[i].dwFlags = pada->Header.AceFlags; + paaNewDeny[i].dwMask = pada->Mask; + paaNewDeny[i].psid = reinterpret_cast(&(pada->SidStart)); + ++cNewDeny; + } + + memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny); + cNewDeny += cDeny; + + + for (i = 0; i < asi.AceCount; ++i) + { + if (!::GetAce(pAcl, i, reinterpret_cast(&paaa))) + { + AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + } + + if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType) + { + continue; // skip non-allowed aces + } + + paaNewAllow[i].dwFlags = paaa->Header.AceFlags; + paaNewAllow[i].dwMask = paaa->Mask; + paaNewAllow[i].psid = reinterpret_cast(&(paaa->SidStart)); + ++cNewAllow; + } + + memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow); + cNewAllow += cAllow; + + // create the dacl with the new + hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew); + AclExitOnFailure(hr, "failed to create new ACL from existing ACL"); + + AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL"); + Assert(S_OK == hr); +LExit: + ReleaseMem(paaNewAllow); + ReleaseMem(paaNewDeny); + + return hr; +} + + +/******************************************************************** +AclMergeDacls - creates a new DACL from two existing ACLs + +********************************************************************/ +extern "C" HRESULT DAPI AclMergeDacls( + __in const ACL* pAcl1, + __in const ACL* pAcl2, + __deref_out ACL** ppAclNew + ) +{ + HRESULT hr = E_NOTIMPL; + + Assert(pAcl1 && pAcl2 && ppAclNew); + UNREFERENCED_PARAMETER(pAcl1); + UNREFERENCED_PARAMETER(pAcl2); + UNREFERENCED_PARAMETER(ppAclNew); + +//LExit: + return hr; +} + + +/******************************************************************** +AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateDaclOld( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out ACL** ppACL + ) +{ + Assert(ppACL); + HRESULT hr = S_OK; + DWORD* pdwAccessMask = NULL; + PSID* ppsid = NULL; + + DWORD i; + int cbAcl; + + *ppACL = NULL; + + // + // create the SIDs and calculate the space for the ACL + // + pdwAccessMask = static_cast(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE)); + AclExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask"); + ppsid = static_cast(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE)); + AclExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid"); + + cbAcl = sizeof (ACL); // start with the size of the header + for (i = 0; i < cAclAccesses; ++i) + { + if (paa[i].pwzAccountName) + { + hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i); + AclExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName); + } + else + { + if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount, + paa[i].nSubAuthority[0], paa[i].nSubAuthority[1], + paa[i].nSubAuthority[2], paa[i].nSubAuthority[3], + paa[i].nSubAuthority[4], paa[i].nSubAuthority[5], + paa[i].nSubAuthority[6], paa[i].nSubAuthority[7], + (void**)(ppsid + i)))) + { + AclExitWithLastError(hr, "failed to initialize SIDs #%u", i); + } + } + + // add the newly allocated SID size to the count of bytes for this ACL + cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD); + if (paa[i].fDenyAccess) + { + cbAcl += sizeof(ACCESS_DENIED_ACE); + } + else + { + cbAcl += sizeof(ACCESS_ALLOWED_ACE); + } + + pdwAccessMask[i] = paa[i].dwAccessMask; + } + + // + // allocate the ACL and set the appropriate ACEs + // + *ppACL = static_cast(MemAlloc(cbAcl, FALSE)); + AclExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to initialize ACLs"); + } + + // add an access-allowed ACE for each of the SIDs + for (i = 0; i < cAclAccesses; ++i) + { + if (paa[i].fDenyAccess) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access denied for ACE"); + } + } + else + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access allowed for ACE"); + } + } + } + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(*ppACL); + } + + if (ppsid) + { + for (i = 0; i < cAclAccesses; ++i) + { + if (ppsid[i]) + { + ::FreeSid(ppsid[i]); + } + } + + MemFree(ppsid); + } + + ReleaseMem(pdwAccessMask); + + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptorFromDacl - creates a self-relative security +descriptor from an existing DACL + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( + __in ACL* pACL, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + + SECURITY_DESCRIPTOR sd; + DWORD cbSD; + + AclExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided"); + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided"); + + *ppsd = NULL; + + // + // create the absolute security descriptor + // + + // initialize our security descriptor, throw the ACL into it, and set the owner +#pragma prefast(push) +#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs +#pragma prefast(disable:25029) + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) || + (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) || + (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE))) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to initialize security descriptor"); + } + + // + // create the self-relative security descriptor + // + cbSD = ::GetSecurityDescriptorLength(&sd); + *ppsd = static_cast(MemAlloc(cbSD, FALSE)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptor - creates a self-relative security descriptor from an +ACL_ACCESS structure + +NOTE: ppsd should be freed with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptor( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + Assert(ppsd); + HRESULT hr = S_OK; + + ACL* pACL; + + *ppsd = NULL; + + // + // create the DACL + // + hr = AclCreateDaclOld(paa, cAclAccesses, &pACL); + AclExitOnFailure(hr, "failed to create DACL for security descriptor"); + + // + // create self-relative security descriptor + // + hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd); + +LExit: + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptorFromString - creates a self-relative security +descriptor from an SDDL string + +NOTE: ppsd should be freed with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString( + __deref_out SECURITY_DESCRIPTOR** ppsd, + __in_z __format_string LPCWSTR wzSddlFormat, + ... + ) +{ + Assert(ppsd); + HRESULT hr = S_OK; + LPWSTR pwzSddl = NULL; + va_list args; + PSECURITY_DESCRIPTOR psd = NULL; + DWORD cbSD = 0; + + *ppsd = NULL; + + va_start(args, wzSddlFormat); + hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args); + va_end(args); + AclExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat); + + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD)) + { + AclExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl); + } + + *ppsd = static_cast(MemAlloc(cbSD, FALSE)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + + Assert(S_OK == hr); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + if (psd) + { + ::LocalFree(psd); + } + + ReleaseStr(pwzSddl); + return hr; +} + + +/******************************************************************** +AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor + +NOTE: passed in security descriptor must be in self-relative format +********************************************************************/ +extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + DWORD cbSD; + + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided"); + *ppsd = NULL; + + // + // create the self-relative security descriptor + // + cbSD = ::GetSecurityDescriptorLength(psd); + *ppsd = static_cast(MemAlloc(cbSD, 0)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + return hr; +} + + +/******************************************************************** +AclGetSecurityDescriptor - returns self-relative security descriptor for named object + +NOTE: free ppsd with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclGetSecurityDescriptor( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + DWORD er; + PSECURITY_DESCRIPTOR psd = NULL; + DWORD cbSD; + + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided"); + *ppsd = NULL; + + // get the security descriptor for the object + er = ::GetNamedSecurityInfoW(const_cast(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd); + AclExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject); + Assert(::IsValidSecurityDescriptor(psd)); + + // copy the self-relative security descriptor + cbSD = ::GetSecurityDescriptorLength(psd); + *ppsd = static_cast(MemAlloc(cbSD, 0)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + if (psd) + { + ::LocalFree(psd); + } + + return hr; +} + + +extern "C" HRESULT DAPI AclSetSecurityWithRetry( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __in_opt PSID psidOwner, + __in_opt PSID psidGroup, + __in_opt PACL pDacl, + __in_opt PACL pSacl, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + HRESULT hr = S_OK; + LPWSTR sczObject = NULL; + DWORD i = 0; + + hr = StrAllocString(&sczObject, wzObject, 0); + AclExitOnFailure(hr, "Failed to copy object to secure."); + + hr = E_FAIL; + for (i = 0; FAILED(hr) && i <= cRetry; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl); + hr = HRESULT_FROM_WIN32(er); + } + AclExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i); + +LExit: + ReleaseStr(sczObject); + + return hr; +} + + +/******************************************************************** +AclFreeSid - frees a SID created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeSid( + __in PSID psid + ) +{ + Assert(psid && ::IsValidSid(psid)); + HRESULT hr = S_OK; + + hr = MemFree(psid); + + return hr; +} + + +/******************************************************************** +AclFreeDacl - frees a DACL created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeDacl( + __in ACL* pACL + ) +{ + Assert(pACL); + HRESULT hr = S_OK; + + hr = MemFree(pACL); + + return hr; +} + + +/******************************************************************** +AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd + ) +{ + Assert(psd && ::IsValidSecurityDescriptor(psd)); + HRESULT hr = S_OK; + + hr = MemFree(psd); + + return hr; +} + + +/******************************************************************** +AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor + +********************************************************************/ +extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor( + __in SECURITY_DESCRIPTOR* pSecurity, + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew + ) +{ + HRESULT hr = S_OK; + PACL pAcl = NULL; + PACL pAclNew = NULL; + BOOL fValid, fDaclDefaulted; + ACL_ACE ace[1]; + SECURITY_DESCRIPTOR* pSecurityNew; + + if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid) + { + AclExitOnLastError(hr, "Failed to get acl from security descriptor"); + } + + hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid); + AclExitOnFailure(hr, "failed to get sid for Administrators group"); + + ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE; + ace[0].dwMask = GENERIC_ALL; + + hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew); + AclExitOnFailure(hr, "failed to add Administrators ACE to ACL"); + + hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew); + AclExitOnLastError(hr, "Failed to create new security descriptor"); + + // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it. + pAclNew = NULL; + + *ppSecurityNew = pSecurityNew; + +LExit: + if (pAclNew) + { + AclFreeDacl(pAclNew); + } + if (ace[0].psid) + { + AclFreeSid(ace[0].psid); + } + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AppExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) +#define AppExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) +#define AppExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) +#define AppExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) +#define AppExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APPUTIL, e, x, s, __VA_ARGS__) +#define AppExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APPUTIL, g, x, s, __VA_ARGS__) + +const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; +typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); +typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); + +extern "C" void DAPI AppFreeCommandLineArgs( + __in LPWSTR* argv + ) +{ + // The "ignored" hack in AppParseCommandLine requires an adjustment. + LPWSTR* argvOriginal = argv - 1; + ::LocalFree(argvOriginal); +} + +/******************************************************************** +AppInitialize - initializes the standard safety precautions for an + installation application. + +********************************************************************/ +extern "C" void DAPI AppInitialize( + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], + __in DWORD cSafelyLoadSystemDlls + ) +{ + HRESULT hr = S_OK; + HMODULE hIgnored = NULL; + BOOL fSetDefaultDllDirectories = FALSE; + + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + + // Best effort call to initialize default DLL directories to system only. + HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32"); + Assert(hKernel32); + LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories"); + if (pfnSetDefaultDllDirectories) + { + if (pfnSetDefaultDllDirectories(PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32)) + { + fSetDefaultDllDirectories = TRUE; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to call SetDefaultDllDirectories."); + } + } + + // Only need to safely load if the default DLL directories was not + // able to be set. + if (!fSetDefaultDllDirectories) + { + // Remove current working directory from search order. + LPFN_SETDLLDIRECTORYW pfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW"); + if (!pfnSetDllDirectory || !pfnSetDllDirectory(L"")) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to call SetDllDirectory."); + } + + for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i) + { + hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored); + if (FAILED(hr)) + { + TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]); + } + } + } +} + +extern "C" void DAPI AppInitializeUnsafe() +{ + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); +} + +extern "C" DAPI_(HRESULT) AppParseCommandLine( + __in LPCWSTR wzCommandLine, + __in int* pArgc, + __in LPWSTR** pArgv + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCommandLine = NULL; + LPWSTR* argv = NULL; + int argc = 0; + + // CommandLineToArgvW tries to treat the first argument as the path to the process, + // which fails pretty miserably if your first argument is something like + // FOO="C:\Program Files\My Company". So give it something harmless to play with. + hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0); + AppExitOnFailure(hr, "Failed to initialize command line."); + + hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0); + AppExitOnFailure(hr, "Failed to copy command line."); + + argv = ::CommandLineToArgvW(sczCommandLine, &argc); + AppExitOnNullWithLastError(argv, hr, "Failed to parse command line."); + + // Skip "ignored" argument/hack. + *pArgv = argv + 1; + *pArgc = argc - 1; + +LExit: + ReleaseStr(sczCommandLine); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define ApupExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) +#define ApupExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) +#define ApupExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) +#define ApupExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) +#define ApupExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APUPUTIL, e, x, s, __VA_ARGS__) +#define ApupExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APUPUTIL, g, x, s, __VA_ARGS__) + +// prototypes +static HRESULT ProcessEntry( + __in ATOM_ENTRY* pAtomEntry, + __in LPCWSTR wzDefaultAppId, + __inout APPLICATION_UPDATE_ENTRY* pApupEntry + ); +static HRESULT ParseEnclosure( + __in ATOM_LINK* pLink, + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ); +static __callback int __cdecl CompareEntries( + void* pvContext, + const void* pvLeft, + const void* pvRight + ); +static HRESULT FilterEntries( + __in APPLICATION_UPDATE_ENTRY* rgEntries, + __in DWORD cEntries, + __in VERUTIL_VERSION* pCurrentVersion, + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, + __inout DWORD* pcFilteredEntries + ); +static HRESULT CopyEntry( + __in const APPLICATION_UPDATE_ENTRY* pSrc, + __in APPLICATION_UPDATE_ENTRY* pDest + ); +static HRESULT CopyEnclosure( + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, + __in APPLICATION_UPDATE_ENCLOSURE* pDest + ); +static void FreeEntry( + __in APPLICATION_UPDATE_ENTRY* pApupEntry + ); +static void FreeEnclosure( + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ); + + +// +// ApupCalculateChainFromAtom - returns the chain of application updates found in an ATOM feed. +// +extern "C" HRESULT DAPI ApupAllocChainFromAtom( + __in ATOM_FEED* pFeed, + __out APPLICATION_UPDATE_CHAIN** ppChain + ) +{ + HRESULT hr = S_OK; + APPLICATION_UPDATE_CHAIN* pChain = NULL; + + pChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); + + // First search the ATOM feed's custom elements to try and find the default application identity. + for (ATOM_UNKNOWN_ELEMENT* pElement = pFeed->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) + { + hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate default application id."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) + { + hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate default application type."); + } + } + } + } + } + + // Assume there will be as many application updates entries as their are feed entries. + if (pFeed->cEntries) + { + pChain->rgEntries = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE)); + ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries."); + + // Process each entry, building up the chain. + for (DWORD i = 0; i < pFeed->cEntries; ++i) + { + hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries); + ApupExitOnFailure(hr, "Failed to process ATOM entry."); + + if (S_FALSE != hr) + { + ++pChain->cEntries; + } + } + + // Sort the chain by descending version and ascending total size. + qsort_s(pChain->rgEntries, pChain->cEntries, sizeof(APPLICATION_UPDATE_ENTRY), CompareEntries, NULL); + } + + // Trim the unused entries from the end, if any of the entries failed to parse or validate + if (pChain->cEntries != pFeed->cEntries) + { + if (pChain->cEntries > 0) + { + pChain->rgEntries = static_cast(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE)); + ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries."); + } + else + { + ReleaseNullMem(pChain->rgEntries); + } + } + + *ppChain = pChain; + pChain = NULL; + +LExit: + ReleaseApupChain(pChain); + + return hr; +} + + +// +// ApupFilterChain - remove the unneeded update elements from the chain. +// +HRESULT DAPI ApupFilterChain( + __in APPLICATION_UPDATE_CHAIN* pChain, + __in VERUTIL_VERSION* pVersion, + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain + ) +{ + HRESULT hr = S_OK; + APPLICATION_UPDATE_CHAIN* pNewChain = NULL; + APPLICATION_UPDATE_ENTRY* prgEntries = NULL; + DWORD cEntries = NULL; + + pNewChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); + ApupExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); + + hr = FilterEntries(pChain->rgEntries, pChain->cEntries, pVersion, &prgEntries, &cEntries); + ApupExitOnFailure(hr, "Failed to filter entries by version."); + + if (pChain->wzDefaultApplicationId) + { + hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0); + ApupExitOnFailure(hr, "Failed to copy default application id."); + } + + if (pChain->wzDefaultApplicationType) + { + hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0); + ApupExitOnFailure(hr, "Failed to copy default application type."); + } + + pNewChain->rgEntries = prgEntries; + pNewChain->cEntries = cEntries; + + *ppFilteredChain = pNewChain; + pNewChain = NULL; + +LExit: + ReleaseApupChain(pNewChain); + return hr; +} + + +// +// ApupFreeChain - frees a previously allocated application update chain. +// +extern "C" void DAPI ApupFreeChain( + __in APPLICATION_UPDATE_CHAIN* pChain + ) +{ + if (pChain) + { + for (DWORD i = 0; i < pChain->cEntries; ++i) + { + FreeEntry(pChain->rgEntries + i); + } + + ReleaseMem(pChain->rgEntries); + ReleaseStr(pChain->wzDefaultApplicationType); + ReleaseStr(pChain->wzDefaultApplicationId); + ReleaseMem(pChain); + } +} + + +static HRESULT ProcessEntry( + __in ATOM_ENTRY* pAtomEntry, + __in LPCWSTR wzDefaultAppId, + __inout APPLICATION_UPDATE_ENTRY* pApupEntry + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + + // First search the ATOM entry's custom elements to try and find the application update information. + for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) + { + hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate application identity."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) + { + hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate application type."); + } + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1)) + { + hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate upgrade id."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1)) + { + hr = VerParseVersion(pAttribute->wzValue, 0, FALSE, &pApupEntry->pUpgradeVersion); + ApupExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzValue, -1, L"true", -1)) + { + pApupEntry->fUpgradeExclusive = TRUE; + } + } + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1)) + { + hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion); + ApupExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); + } + } + } + + // If there is no application identity or no version, skip the whole thing. + if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !pApupEntry->pVersion) + { + ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version. + } + + if (pApupEntry->pUpgradeVersion) + { + hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare version to upgrade version."); + + if (nCompareResult >= 0) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); + } + } + + if (pAtomEntry->wzTitle) + { + hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0); + ApupExitOnFailure(hr, "Failed to allocate application title."); + } + + if (pAtomEntry->wzSummary) + { + hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0); + ApupExitOnFailure(hr, "Failed to allocate application summary."); + } + + if (pAtomEntry->pContent) + { + if (pAtomEntry->pContent->wzType) + { + hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0); + ApupExitOnFailure(hr, "Failed to allocate content type."); + } + + if (pAtomEntry->pContent->wzValue) + { + hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate content."); + } + } + // Now process the enclosures. Assume every link in the ATOM entry is an enclosure. + pApupEntry->rgEnclosures = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE)); + ApupExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry."); + + for (DWORD i = 0; i < pAtomEntry->cLinks; ++i) + { + ATOM_LINK* pLink = pAtomEntry->rgLinks + i; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1)) + { + hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures); + ApupExitOnFailure(hr, "Failed to parse enclosure."); + + pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures + + ++pApupEntry->cEnclosures; + } + } + +LExit: + if (S_OK != hr) // if anything went wrong, free the entry. + { + FreeEntry(pApupEntry); + memset(pApupEntry, 0, sizeof(APPLICATION_UPDATE_ENTRY)); + } + + return hr; +} + + +static HRESULT ParseEnclosure( + __in ATOM_LINK* pLink, + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ) +{ + HRESULT hr = S_OK; + DWORD dwDigestLength = 0; + DWORD dwDigestStringLength = 0; + size_t cchDigestString = 0; + + // First search the ATOM link's custom elements to try and find the application update enclosure information. + for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1)) + { + // Find the digest[@algorithm] which is required. Everything else is ignored. + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + dwDigestLength = 0; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5; + dwDigestLength = MD5_HASH_LEN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1; + dwDigestLength = SHA1_HASH_LEN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256; + dwDigestLength = SHA256_HASH_LEN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha512", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA512; + dwDigestLength = SHA512_HASH_LEN; + } + break; + } + } + + if (dwDigestLength) + { + dwDigestStringLength = 2 * dwDigestLength; + + hr = ::StringCchLengthW(pElement->wzValue, STRSAFE_MAX_CCH, &cchDigestString); + ApupExitOnFailure(hr, "Failed to get string length of digest value."); + + if (dwDigestStringLength != cchDigestString) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ApupExitOnRootFailure(hr, "Invalid digest length (%Iu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); + } + + pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength; + pEnclosure->rgbDigest = static_cast(MemAlloc(pEnclosure->cbDigest, TRUE)); + ApupExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); + + hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest); + ApupExitOnFailure(hr, "Failed to decode digest value."); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ApupExitOnRootFailure(hr, "Unknown algorithm type for digest."); + } + + break; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1)) + { + hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to copy local name."); + } + } + } + + pEnclosure->dw64Size = pLink->dw64Length; + + hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0); + ApupExitOnFailure(hr, "Failed to allocate enclosure URL."); + + pEnclosure->fInstaller = FALSE; + pEnclosure->wzLocalName = NULL; + +LExit: + return hr; +} + + +static __callback int __cdecl CompareEntries( + void* /*pvContext*/, + const void* pvLeft, + const void* pvRight + ) +{ + int ret = 0; + const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast(pvLeft); + const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast(pvRight); + + VerCompareParsedVersions(pEntryLeft->pVersion, pEntryRight->pVersion, &ret); + if (0 == ret) + { + VerCompareParsedVersions(pEntryLeft->pUpgradeVersion, pEntryRight->pUpgradeVersion, &ret); + if (0 == ret) + { + ret = (pEntryLeft->dw64TotalSize < pEntryRight->dw64TotalSize) ? -1 : + (pEntryLeft->dw64TotalSize > pEntryRight->dw64TotalSize) ? 1 : 0; + } + } + + // Sort descending. + ret = -ret; + + return ret; +} + + +static HRESULT FilterEntries( + __in APPLICATION_UPDATE_ENTRY* rgEntries, + __in DWORD cEntries, + __in VERUTIL_VERSION* pCurrentVersion, + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, + __inout DWORD* pcFilteredEntries + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + size_t cbAllocSize = 0; + const APPLICATION_UPDATE_ENTRY* pRequired = NULL;; + LPVOID pv = NULL; + + if (cEntries) + { + for (DWORD i = 0; i < cEntries; ++i) + { + const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i; + + hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare versions."); + + if (nCompareResult >= 0) + { + continue; + } + + hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pUpgradeVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare upgrade versions."); + + if (nCompareResult > 0 || (!pEntry->fUpgradeExclusive && nCompareResult == 0)) + { + pRequired = pEntry; + break; + } + } + + if (pRequired) + { + DWORD cNewFilteredEntries = *pcFilteredEntries + 1; + + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize); + ApupExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries); + + if (*prgFilteredEntries) + { + pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE); + ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries."); + } + else + { + pv = MemAlloc(cbAllocSize, TRUE); + ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries."); + } + + *pcFilteredEntries = cNewFilteredEntries; + *prgFilteredEntries = static_cast(pv); + pv = NULL; + + hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1); + ApupExitOnFailure(hr, "Failed to deep copy entry."); + + hr = VerCompareParsedVersions(pRequired->pVersion, rgEntries[0].pVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare required version."); + + if (nCompareResult < 0) + { + FilterEntries(rgEntries, cEntries, pRequired->pVersion, prgFilteredEntries, pcFilteredEntries); + } + } + } + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT CopyEntry( + __in const APPLICATION_UPDATE_ENTRY* pSrc, + __in APPLICATION_UPDATE_ENTRY* pDest + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENTRY)); + + if (pSrc->wzApplicationId) + { + hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0); + ApupExitOnFailure(hr, "Failed to copy application id."); + } + + if (pSrc->wzApplicationType) + { + hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0); + ApupExitOnFailure(hr, "Failed to copy application type."); + } + + if (pSrc->wzUpgradeId) + { + hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0); + ApupExitOnFailure(hr, "Failed to copy upgrade id."); + } + + if (pSrc->wzTitle) + { + hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0); + ApupExitOnFailure(hr, "Failed to copy title."); + } + + if (pSrc->wzSummary) + { + hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0); + ApupExitOnFailure(hr, "Failed to copy summary."); + } + + if (pSrc->wzContentType) + { + hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0); + ApupExitOnFailure(hr, "Failed to copy content type."); + } + + if (pSrc->wzContent) + { + hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0); + ApupExitOnFailure(hr, "Failed to copy content."); + } + + pDest->dw64TotalSize = pSrc->dw64TotalSize; + + hr = VerCopyVersion(pSrc->pUpgradeVersion, &pDest->pUpgradeVersion); + ApupExitOnFailure(hr, "Failed to copy upgrade version."); + + hr = VerCopyVersion(pSrc->pVersion, &pDest->pVersion); + ApupExitOnFailure(hr, "Failed to copy version."); + + pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive; + + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize); + ApupExitOnRootFailure(hr, "Overflow while calculating memory allocation size"); + + pDest->rgEnclosures = static_cast(MemAlloc(cbAllocSize, TRUE)); + ApupExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures."); + + pDest->cEnclosures = pSrc->cEnclosures; + + for (DWORD i = 0; i < pDest->cEnclosures; ++i) + { + hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i); + ApupExitOnFailure(hr, "Failed to copy enclosure."); + } + +LExit: + if (FAILED(hr)) + { + FreeEntry(pDest); + } + + return hr; +} + + +static HRESULT CopyEnclosure( + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, + __in APPLICATION_UPDATE_ENCLOSURE* pDest + ) +{ + HRESULT hr = S_OK; + + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENCLOSURE)); + + if (pSrc->wzUrl) + { + hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0); + ApupExitOnFailure(hr, "Failed copy url."); + } + + if (pSrc->wzLocalName) + { + hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0); + ApupExitOnFailure(hr, "Failed copy url."); + } + + pDest->rgbDigest = static_cast(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE)); + ApupExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest."); + + pDest->cbDigest = pSrc->cbDigest; + + memcpy_s(pDest->rgbDigest, sizeof(BYTE) * pDest->cbDigest, pSrc->rgbDigest, sizeof(BYTE) * pSrc->cbDigest); + + pDest->digestAlgorithm = pSrc->digestAlgorithm; + + pDest->dw64Size = pSrc->dw64Size; + pDest->fInstaller = pSrc->fInstaller; + +LExit: + if (FAILED(hr)) + { + FreeEnclosure(pDest); + } + + return hr; +} + + +static void FreeEntry( + __in APPLICATION_UPDATE_ENTRY* pEntry + ) +{ + if (pEntry) + { + for (DWORD i = 0; i < pEntry->cEnclosures; ++i) + { + FreeEnclosure(pEntry->rgEnclosures + i); + } + + ReleaseStr(pEntry->wzUpgradeId); + ReleaseStr(pEntry->wzApplicationType); + ReleaseStr(pEntry->wzApplicationId); + ReleaseStr(pEntry->wzTitle); + ReleaseStr(pEntry->wzSummary); + ReleaseStr(pEntry->wzContentType); + ReleaseStr(pEntry->wzContent); + ReleaseVerutilVersion(pEntry->pVersion); + ReleaseVerutilVersion(pEntry->pUpgradeVersion); + } +} + + +static void FreeEnclosure( + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ) +{ + if (pEnclosure) + { + ReleaseMem(pEnclosure->rgbDigest); + ReleaseStr(pEnclosure->wzLocalName); + ReleaseStr(pEnclosure->wzUrl); + } +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AtomExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) +#define AtomExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) +#define AtomExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) +#define AtomExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) +#define AtomExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ATOMUTIL, e, x, s, __VA_ARGS__) +#define AtomExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ATOMUTIL, g, x, s, __VA_ARGS__) + +static HRESULT ParseAtomDocument( + __in IXMLDOMDocument *pixd, + __out ATOM_FEED **ppFeed + ); +static HRESULT ParseAtomFeed( + __in IXMLDOMNode *pixnFeed, + __out ATOM_FEED **ppFeed + ); +static HRESULT ParseAtomAuthor( + __in IXMLDOMNode* pixnAuthor, + __in ATOM_AUTHOR* pAuthor + ); +static HRESULT ParseAtomCategory( + __in IXMLDOMNode* pixnCategory, + __in ATOM_CATEGORY* pCategory + ); +static HRESULT ParseAtomEntry( + __in IXMLDOMNode* pixnEntry, + __in ATOM_ENTRY* pEntry + ); +static HRESULT ParseAtomLink( + __in IXMLDOMNode* pixnLink, + __in ATOM_LINK* pLink + ); +static HRESULT ParseAtomUnknownElement( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement + ); +static HRESULT ParseAtomUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ); +static HRESULT AssignDateTime( + __in FILETIME* pft, + __in IXMLDOMNode* pNode + ); +static HRESULT AssignString( + __out_z LPWSTR* pwzValue, + __in IXMLDOMNode* pNode + ); +static void FreeAtomAuthor( + __in_opt ATOM_AUTHOR* pAuthor + ); +static void FreeAtomContent( + __in_opt ATOM_CONTENT* pContent + ); +static void FreeAtomCategory( + __in_opt ATOM_CATEGORY* pCategory + ); +static void FreeAtomEntry( + __in_opt ATOM_ENTRY* pEntry + ); +static void FreeAtomLink( + __in_opt ATOM_LINK* pLink + ); +static void FreeAtomUnknownElementList( + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement + ); +static void FreeAtomUnknownAttributeList( + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ); + +template static HRESULT AllocateAtomType( + __in IXMLDOMNode* pixnParent, + __in LPCWSTR wzT, + __out T** pprgT, + __out DWORD* pcT + ); + + +/******************************************************************** + AtomInitialize - Initialize ATOM utilities. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomInitialize() +{ + return XmlInitialize(); +} + + +/******************************************************************** + AtomUninitialize - Uninitialize ATOM utilities. + +*********************************************************************/ +extern "C" void DAPI AtomUninitialize() +{ + XmlUninitialize(); +} + + +/******************************************************************** + AtomParseFromString - parses out an ATOM feed from a string. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromString( + __in_z LPCWSTR wzAtomString, + __out ATOM_FEED **ppFeed + ) +{ + Assert(wzAtomString); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + IXMLDOMDocument *pixdAtom = NULL; + + hr = XmlLoadDocument(wzAtomString, &pixdAtom); + AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); + + hr = ParseAtomDocument(pixdAtom, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + ReleaseObject(pixdAtom); + + return hr; +} + + +/******************************************************************** + AtomParseFromFile - parses out an ATOM feed from a file path. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromFile( + __in_z LPCWSTR wzAtomFile, + __out ATOM_FEED **ppFeed + ) +{ + Assert(wzAtomFile); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + IXMLDOMDocument *pixdAtom = NULL; + + hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom); + AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); + + hr = ParseAtomDocument(pixdAtom, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + ReleaseObject(pixdAtom); + + return hr; +} + + +/******************************************************************** + AtomParseFromDocument - parses out an ATOM feed from an XML document. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromDocument( + __in IXMLDOMDocument* pixdDocument, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixdDocument); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + + hr = ParseAtomDocument(pixdDocument, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + AtomFreeFeed - parses out an ATOM feed from a string. + +*********************************************************************/ +extern "C" void DAPI AtomFreeFeed( + __in_xcount(pFeed->cItems) ATOM_FEED* pFeed + ) +{ + if (pFeed) + { + FreeAtomUnknownElementList(pFeed->pUnknownElements); + ReleaseObject(pFeed->pixn); + + for (DWORD i = 0; i < pFeed->cLinks; ++i) + { + FreeAtomLink(pFeed->rgLinks + i); + } + ReleaseMem(pFeed->rgLinks); + + for (DWORD i = 0; i < pFeed->cEntries; ++i) + { + FreeAtomEntry(pFeed->rgEntries + i); + } + ReleaseMem(pFeed->rgEntries); + + for (DWORD i = 0; i < pFeed->cCategories; ++i) + { + FreeAtomCategory(pFeed->rgCategories + i); + } + ReleaseMem(pFeed->rgCategories); + + for (DWORD i = 0; i < pFeed->cAuthors; ++i) + { + FreeAtomAuthor(pFeed->rgAuthors + i); + } + ReleaseMem(pFeed->rgAuthors); + + ReleaseStr(pFeed->wzGenerator); + ReleaseStr(pFeed->wzIcon); + ReleaseStr(pFeed->wzId); + ReleaseStr(pFeed->wzLogo); + ReleaseStr(pFeed->wzSubtitle); + ReleaseStr(pFeed->wzTitle); + + MemFree(pFeed); + } +} + + +/******************************************************************** + ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document. + +*********************************************************************/ +static HRESULT ParseAtomDocument( + __in IXMLDOMDocument *pixd, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixd); + Assert(ppFeed); + + HRESULT hr = S_OK; + IXMLDOMElement *pFeedElement = NULL; + + ATOM_FEED *pNewFeed = NULL; + + // + // Get the document element and start processing feeds. + // + hr = pixd->get_documentElement(&pFeedElement); + AtomExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); + + hr = ParseAtomFeed(pFeedElement, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM feed."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseObject(pFeedElement); + + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element. + +*********************************************************************/ +static HRESULT ParseAtomFeed( + __in IXMLDOMNode *pixnFeed, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixnFeed); + Assert(ppFeed); + + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + ATOM_FEED *pNewFeed = NULL; + DWORD cAuthors = 0; + DWORD cCategories = 0; + DWORD cEntries = 0; + DWORD cLinks = 0; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // First, allocate the new feed and all the possible sub elements. + pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE); + AtomExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); + + pNewFeed->pixn = pixnFeed; + pNewFeed->pixn->AddRef(); + + hr = AllocateAtomType(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed authors."); + + hr = AllocateAtomType(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed categories."); + + hr = AllocateAtomType(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed entries."); + + hr = AllocateAtomType(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed links."); + + // Second, process the elements under a feed. + hr = pixnFeed->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1)) + { + hr = AssignString(&pNewFeed->wzGenerator, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed generator."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1)) + { + hr = AssignString(&pNewFeed->wzIcon, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed icon."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) + { + hr = AssignString(&pNewFeed->wzId, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed id."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1)) + { + hr = AssignString(&pNewFeed->wzLogo, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed logo."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1)) + { + hr = AssignString(&pNewFeed->wzSubtitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pNewFeed->wzTitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) + { + hr = AssignDateTime(&pNewFeed->ftUpdated, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed updated."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) + { + hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]); + AtomExitOnFailure(hr, "Failed to parse ATOM author."); + + ++cAuthors; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) + { + hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]); + AtomExitOnFailure(hr, "Failed to parse ATOM category."); + + ++cCategories; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1)) + { + hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry."); + + ++cEntries; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) + { + hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]); + AtomExitOnFailure(hr, "Failed to parse ATOM link."); + + ++cLinks; + } + else + { + hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + if (!pNewFeed->wzId || !*pNewFeed->wzId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/id element."); + } + else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/title element."); + } + else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/updated element."); + } + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + AllocateAtomType - allocates enough space for all of the ATOM elements + of a particular type under a particular node. + +*********************************************************************/ +template static HRESULT AllocateAtomType( + __in IXMLDOMNode* pixnParent, + __in LPCWSTR wzT, + __out T** pprgT, + __out DWORD* pcT + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + long cT = 0; + T* prgT = NULL; + + hr = XmlSelectNodes(pixnParent, wzT, &pNodeList); + AtomExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); + + if (S_OK == hr) + { + hr = pNodeList->get_length(&cT); + AtomExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); + + if (cT == 0) + { + ExitFunction(); + } + + prgT = static_cast(MemAlloc(sizeof(T) * cT, TRUE)); + AtomExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); + + *pcT = cT; + *pprgT = prgT; + prgT = NULL; + } + else + { + *pprgT = NULL; + *pcT = 0; + } + +LExit: + ReleaseMem(prgT); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomAuthor( + __in IXMLDOMNode* pixnAuthor, + __in ATOM_AUTHOR* pAuthor + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + hr = pixnAuthor->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1)) + { + hr = AssignString(&pAuthor->wzName, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM author name."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1)) + { + hr = AssignString(&pAuthor->wzEmail, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM author email."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1)) + { + hr = AssignString(&pAuthor->wzUrl, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM author uri."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM author elements."); + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomCategory( + __in IXMLDOMNode* pixnCategory, + __in ATOM_CATEGORY* pCategory + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnCategory->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1)) + { + hr = AssignString(&pCategory->wzLabel, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM category label."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1)) + { + hr = AssignString(&pCategory->wzScheme, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM category scheme."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1)) + { + hr = AssignString(&pCategory->wzTerm, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM category term."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM category attributes."); + + // Process elements second. + hr = pixnCategory->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM category elements."); + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomContent - parses out an ATOM content from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomContent( + __in IXMLDOMNode* pixnContent, + __in ATOM_CONTENT* pContent + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnContent->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) + { + hr = AssignString(&pContent->wzType, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM content type."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1)) + { + hr = AssignString(&pContent->wzUrl, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM content scheme."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM content attributes."); + + // Process elements second. + hr = pixnContent->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM content elements."); + + hr = AssignString(&pContent->wzValue, pixnContent); + AtomExitOnFailure(hr, "Failed to allocate ATOM content value."); + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomEntry( + __in IXMLDOMNode* pixnEntry, + __in ATOM_ENTRY* pEntry + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + DWORD cAuthors = 0; + DWORD cCategories = 0; + DWORD cLinks = 0; + + pEntry->pixn = pixnEntry; + pEntry->pixn->AddRef(); + + // First, allocate all the possible sub elements. + hr = AllocateAtomType(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry authors."); + + hr = AllocateAtomType(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry categories."); + + hr = AllocateAtomType(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry links."); + + // Second, process elements. + hr = pixnEntry->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) + { + hr = AssignString(&pEntry->wzId, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry id."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1)) + { + hr = AssignString(&pEntry->wzSummary, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry summary."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pEntry->wzTitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1)) + { + hr = AssignDateTime(&pEntry->ftPublished, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry published."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) + { + hr = AssignDateTime(&pEntry->ftUpdated, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry updated."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) + { + hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry author."); + + ++cAuthors; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) + { + hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry category."); + + ++cCategories; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1)) + { + if (NULL != pEntry->pContent) + { + hr = E_UNEXPECTED; + AtomExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); + } + + pEntry->pContent = static_cast(MemAlloc(sizeof(ATOM_CONTENT), TRUE)); + AtomExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); + + hr = ParseAtomContent(pNode, pEntry->pContent); + AtomExitOnFailure(hr, "Failed to parse ATOM entry content."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) + { + hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry link."); + + ++cLinks; + } + else + { + hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM entry elements."); + + if (!pEntry->wzId || !*pEntry->wzId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); + } + else if (!pEntry->wzTitle || !*pEntry->wzTitle) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); + } + else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); + } + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomLink - parses out an ATOM link from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomLink( + __in IXMLDOMNode* pixnLink, + __in ATOM_LINK* pLink + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnLink->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes for ATOM link."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1)) + { + hr = AssignString(&pLink->wzRel, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link rel."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1)) + { + hr = AssignString(&pLink->wzUrl, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link href."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1)) + { + hr = XmlGetAttributeLargeNumber(pixnLink, bstrNodeName, &pLink->dw64Length); + if (E_INVALIDARG == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + AtomExitOnFailure(hr, "Failed to parse ATOM link length."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pLink->wzTitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) + { + hr = AssignString(&pLink->wzType, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link type."); + } + else + { + hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM link attributes."); + + // Process elements second. + hr = pixnLink->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM link elements."); + + hr = AssignString(&pLink->wzValue, pixnLink); + AtomExitOnFailure(hr, "Failed to allocate ATOM link value."); + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomUnknownElement( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement + ) +{ + Assert(ppUnknownElement); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + ATOM_UNKNOWN_ELEMENT* pNewUnknownElement; + + pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE); + AtomExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + AtomExitOnFailure(hr, "Failed to get unknown element namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + AtomExitOnFailure(hr, "Failed to get unknown element name."); + + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + AtomExitOnFailure(hr, "Failed to get unknown element value."); + + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); + + hr = pNode->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) + { + hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); + AtomExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); + + ReleaseNullObject(pixnAttribute); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + AtomExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); + + ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownElement; + pNewUnknownElement = NULL; + +LExit: + FreeAtomUnknownElementList(pNewUnknownElement); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + + return hr; +} + + +/******************************************************************** + ParseAtomUnknownAttribute - parses out attribute from an unknown element + +*********************************************************************/ +static HRESULT ParseAtomUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ) +{ + Assert(ppUnknownAttribute); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; + + pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE); + AtomExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + AtomExitOnFailure(hr, "Failed to get unknown attribute namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + AtomExitOnFailure(hr, "Failed to get unknown attribute name."); + + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + AtomExitOnFailure(hr, "Failed to get unknown attribute value."); + + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); + + ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownAttribute; + pNewUnknownAttribute = NULL; + +LExit: + FreeAtomUnknownAttributeList(pNewUnknownAttribute); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + + return hr; +} + + +/******************************************************************** + AssignDateTime - assigns the value of a node to a FILETIME struct. + +*********************************************************************/ +static HRESULT AssignDateTime( + __in FILETIME* pft, + __in IXMLDOMNode* pNode + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Already process this datetime value."); + } + + hr = XmlGetText(pNode, &bstrValue); + AtomExitOnFailure(hr, "Failed to get value."); + + if (S_OK == hr) + { + hr = TimeFromString3339(bstrValue, pft); + AtomExitOnFailure(hr, "Failed to convert value to time."); + } + else + { + ZeroMemory(pft, sizeof(FILETIME)); + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrValue); + + return hr; +} + + +/******************************************************************** + AssignString - assigns the value of a node to a dynamic string. + +*********************************************************************/ +static HRESULT AssignString( + __out_z LPWSTR* pwzValue, + __in IXMLDOMNode* pNode + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + if (pwzValue && *pwzValue) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Already processed this value."); + } + + hr = XmlGetText(pNode, &bstrValue); + AtomExitOnFailure(hr, "Failed to get value."); + + if (S_OK == hr) + { + hr = StrAllocString(pwzValue, bstrValue, 0); + AtomExitOnFailure(hr, "Failed to allocate value."); + } + else + { + ReleaseNullStr(pwzValue); + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrValue); + + return hr; +} + + +/******************************************************************** + FreeAtomAuthor - releases all of the memory used by an ATOM author. + +*********************************************************************/ +static void FreeAtomAuthor( + __in_opt ATOM_AUTHOR* pAuthor + ) +{ + if (pAuthor) + { + ReleaseStr(pAuthor->wzUrl); + ReleaseStr(pAuthor->wzEmail); + ReleaseStr(pAuthor->wzName); + } +} + + +/******************************************************************** + FreeAtomCategory - releases all of the memory used by an ATOM category. + +*********************************************************************/ +static void FreeAtomCategory( + __in_opt ATOM_CATEGORY* pCategory + ) +{ + if (pCategory) + { + FreeAtomUnknownElementList(pCategory->pUnknownElements); + + ReleaseStr(pCategory->wzTerm); + ReleaseStr(pCategory->wzScheme); + ReleaseStr(pCategory->wzLabel); + } +} + + +/******************************************************************** + FreeAtomContent - releases all of the memory used by an ATOM content. + +*********************************************************************/ +static void FreeAtomContent( + __in_opt ATOM_CONTENT* pContent + ) +{ + if (pContent) + { + FreeAtomUnknownElementList(pContent->pUnknownElements); + + ReleaseStr(pContent->wzValue); + ReleaseStr(pContent->wzUrl); + ReleaseStr(pContent->wzType); + } +} + + +/******************************************************************** + FreeAtomEntry - releases all of the memory used by an ATOM entry. + +*********************************************************************/ +static void FreeAtomEntry( + __in_opt ATOM_ENTRY* pEntry + ) +{ + if (pEntry) + { + FreeAtomUnknownElementList(pEntry->pUnknownElements); + ReleaseObject(pEntry->pixn); + + for (DWORD i = 0; i < pEntry->cLinks; ++i) + { + FreeAtomLink(pEntry->rgLinks + i); + } + ReleaseMem(pEntry->rgLinks); + + for (DWORD i = 0; i < pEntry->cCategories; ++i) + { + FreeAtomCategory(pEntry->rgCategories + i); + } + ReleaseMem(pEntry->rgCategories); + + for (DWORD i = 0; i < pEntry->cAuthors; ++i) + { + FreeAtomAuthor(pEntry->rgAuthors + i); + } + ReleaseMem(pEntry->rgAuthors); + + FreeAtomContent(pEntry->pContent); + ReleaseMem(pEntry->pContent); + + ReleaseStr(pEntry->wzTitle); + ReleaseStr(pEntry->wzSummary); + ReleaseStr(pEntry->wzId); + } +} + + +/******************************************************************** + FreeAtomLink - releases all of the memory used by an ATOM link. + +*********************************************************************/ +static void FreeAtomLink( + __in_opt ATOM_LINK* pLink + ) +{ + if (pLink) + { + FreeAtomUnknownElementList(pLink->pUnknownElements); + FreeAtomUnknownAttributeList(pLink->pUnknownAttributes); + + ReleaseStr(pLink->wzValue); + ReleaseStr(pLink->wzUrl); + ReleaseStr(pLink->wzType); + ReleaseStr(pLink->wzTitle); + ReleaseStr(pLink->wzRel); + } +} + + +/******************************************************************** + FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements + +*********************************************************************/ +static void FreeAtomUnknownElementList( + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement + ) +{ + while (pUnknownElement) + { + ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement; + pUnknownElement = pUnknownElement->pNext; + + FreeAtomUnknownAttributeList(pFree->pAttributes); + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzElement); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} + + +/******************************************************************** + FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes + +*********************************************************************/ +static void FreeAtomUnknownAttributeList( + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ) +{ + while (pUnknownAttribute) + { + ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; + pUnknownAttribute = pUnknownAttribute->pNext; + + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzAttribute); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define BuffExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) +#define BuffExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) +#define BuffExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) +#define BuffExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) +#define BuffExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUFFUTIL, e, x, s, __VA_ARGS__) +#define BuffExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_BUFFUTIL, g, x, s, __VA_ARGS__) + + +// constants + +#define BUFFER_INCREMENT 128 + + +// helper function declarations + +static HRESULT EnsureBufferSize( + __deref_inout_bcount(cbSize) BYTE** ppbBuffer, + __in SIZE_T cbSize + ); + + +// functions + +extern "C" HRESULT BuffReadNumber( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD* pdw + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw = *(const DWORD*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadNumber64( + __in_bcount(cbBuffer) const BYTE * pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD64* pdw64 + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw64); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD64) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD64); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadPointer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD_PTR* pdw64 + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw64); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD_PTR) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw64 = *(const DWORD_PTR*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD_PTR); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadString( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPWSTR* pscz + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pscz); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + + // verify buffer size + if (sizeof(SIZE_T) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + // read character count + cch = *(const SIZE_T*)(pbBuffer + *piBuffer); + + hr = ::SIZETMult(cch, sizeof(WCHAR), &cb); + BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + + hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); + BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small to hold character data."); + } + + // copy character data + hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch); + BuffExitOnFailure(hr, "Failed to copy character data."); + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadStringAnsi( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPSTR* pscz + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pscz); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + + // verify buffer size + if (sizeof(SIZE_T) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + // read character count + cch = *(const SIZE_T*)(pbBuffer + *piBuffer); + + hr = ::SIZETMult(cch, sizeof(CHAR), &cb); + BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + + hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); + BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small to hold character count."); + } + + // copy character data + hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch); + BuffExitOnFailure(hr, "Failed to copy character data."); + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadStream( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_inout_bcount(*pcbStream) BYTE** ppbStream, + __out SIZE_T* pcbStream + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(ppbStream); + Assert(pcbStream); + + HRESULT hr = S_OK; + SIZE_T cb = 0; + SIZE_T cbAvailable = 0; + errno_t err = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); + + // verify buffer size + if (sizeof(SIZE_T) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + // read stream size + cb = *(const SIZE_T*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(SIZE_T); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small to hold byte count."); + } + + // allocate buffer + *ppbStream = (BYTE*)MemAlloc(cb, TRUE); + BuffExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); + + // read stream data + err = memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to read stream from buffer, error: %d", err); + } + + *piBuffer += cb; + + // return stream size + *pcbStream = cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteNumber( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD dw + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD*)(*ppbBuffer + *piBuffer) = dw; + *piBuffer += sizeof(DWORD); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteNumber64( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD64 dw64 + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64; + *piBuffer += sizeof(DWORD64); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWritePointer( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD_PTR dw + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD_PTR)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; + *piBuffer += sizeof(DWORD_PTR); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteString( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCWSTR scz + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + errno_t err = 0; + + if (scz) + { + hr = ::StringCchLengthW(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); + BuffExitOnRootFailure(hr, "Failed to get string size.") + } + + cb = cch * sizeof(WCHAR); + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy character count to buffer + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(SIZE_T); + + // copy data to buffer + err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%ls', error: %d", scz, err); + } + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteStringAnsi( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCSTR scz + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + errno_t err = 0; + + if (scz) + { + hr = ::StringCchLengthA(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); + BuffExitOnRootFailure(hr, "Failed to get string size.") + } + + cb = cch * sizeof(CHAR); + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy character count to buffer + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(SIZE_T); + + // copy data to buffer + err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%hs', error: %d", scz, err); + } + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteStream( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_bcount(cbStream) const BYTE* pbStream, + __in SIZE_T cbStream + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + Assert(pbStream); + + HRESULT hr = S_OK; + SIZE_T cb = cbStream; + errno_t err = 0; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(SIZE_T)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy byte count to buffer + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cb; + *piBuffer += sizeof(SIZE_T); + + // copy data to buffer + err = memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write stream to buffer, error: %d", err); + } + + *piBuffer += cbStream; + +LExit: + return hr; +} + + +// helper functions + +static HRESULT EnsureBufferSize( + __deref_inout_bcount(cbSize) BYTE** ppbBuffer, + __in SIZE_T cbSize + ) +{ + HRESULT hr = S_OK; + SIZE_T cbTarget = ((cbSize / BUFFER_INCREMENT) + 1) * BUFFER_INCREMENT; + + if (*ppbBuffer) + { + if (MemSize(*ppbBuffer) < cbTarget) + { + LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE); + BuffExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer."); + *ppbBuffer = (BYTE*)pv; + } + } + else + { + *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE); + BuffExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); + } + +LExit: + return hr; +} 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 @@ + + + + + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + + + $(MSBuildThisFileDirectory)native\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + + + $(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + + + $(MSBuildThisFileDirectory)native\v142\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__) + +// constants +// From engine/registration.h +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; + +// Forward declarations. +/******************************************************************** +OpenBundleKey - Opens the bundle uninstallation key for a given bundle + +NOTE: caller is responsible for closing key +********************************************************************/ +static HRESULT OpenBundleKey( + __in_z LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key); + + +extern "C" HRESULT DAPI BundleGetBundleInfo( + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzAttribute, + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, + __inout_opt LPDWORD pcchValueBuf + ) +{ + Assert(wzBundleId && wzAttribute); + + HRESULT hr = S_OK; + BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE; + LPWSTR sczValue = NULL; + HKEY hkBundle = NULL; + DWORD cchSource = 0; + DWORD dwType = 0; + DWORD dwValue = 0; + + if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute) + { + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && + FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) + { + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); + } + + // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY + hr = RegGetType(hkBundle, wzAttribute, &dwType); + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, wzAttribute, &sczValue); + ButilExitOnFailure(hr, "Failed to read string property."); + break; + case REG_DWORD: + hr = RegReadNumber(hkBundle, wzAttribute, &dwValue); + ButilExitOnFailure(hr, "Failed to read dword property."); + + hr = StrAllocFormatted(&sczValue, L"%d", dwValue); + ButilExitOnFailure(hr, "Failed to format dword property as string."); + break; + default: + ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); + + } + + hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + ButilExitOnFailure(hr, "Failed to calculate length of string"); + + if (lpValueBuf) + { + // cchSource is the length of the string not including the terminating null character + if (*pcchValueBuf <= cchSource) + { + *pcchValueBuf = ++cchSource; + ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); + } + + hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + + *pcchValueBuf = cchSource++; + } + +LExit: + ReleaseRegKey(hkBundle); + ReleaseStr(sczValue); + + return hr; +} + +HRESULT DAPI BundleEnumRelatedBundle( + __in_z LPCWSTR wzUpgradeCode, + __in BUNDLE_INSTALL_CONTEXT context, + __inout PDWORD pdwStartIndex, + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + HKEY hkUninstall = NULL; + HKEY hkBundle = NULL; + LPWSTR sczUninstallSubKey = NULL; + DWORD cchUninstallSubKey = 0; + LPWSTR sczUninstallSubKeyPath = NULL; + LPWSTR sczValue = NULL; + DWORD dwType = 0; + + LPWSTR* rgsczBundleUpgradeCodes = NULL; + DWORD cBundleUpgradeCodes = 0; + BOOL fUpgradeCodeFound = FALSE; + + if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex) + { + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); + + for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++) + { + hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey); + ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); + + hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle); + ButilExitOnFailure(hr, "Failed to open uninstall key path."); + + // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ + hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); + if (FAILED(hr)) + { + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + // Not a bundle + continue; + } + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + + ReleaseNullStr(sczValue); + + break; + case REG_MULTI_SZ: + hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); + + for (DWORD i = 0; i < cBundleUpgradeCodes; i++) + { + LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i]; + if (wzBundleUpgradeCode && *wzBundleUpgradeCode) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + } + } + ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + break; + + default: + ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); + + } + + if (fUpgradeCodeFound) + { + if (lpBundleIdBuf) + { + hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast(&cchUninstallSubKey)); + ButilExitOnFailure(hr, "Failed to calculate length of string"); + + hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + } + + break; + } + + // Cleanup before next iteration + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + } + +LExit: + ReleaseStr(sczValue); + ReleaseStr(sczUninstallSubKey); + ReleaseStr(sczUninstallSubKeyPath); + ReleaseRegKey(hkBundle); + ReleaseRegKey(hkUninstall); + ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + return hr; +} + + +HRESULT OpenBundleKey( + __in_z LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key) +{ + Assert(key && wzBundleId); + AssertSz(NULL == *key, "*key should be null"); + + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + LPWSTR sczKeypath = NULL; + + hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); + +LExit: + ReleaseStr(sczKeypath); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CabcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) +#define CabcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) +#define CabcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) +#define CabcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) +#define CabcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABCUTIL, e, x, s, __VA_ARGS__) +#define CabcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABCUTIL, g, x, s, __VA_ARGS__) + + +static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?'; +static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024; + +// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder() +// too often (because of duplicates too close together) we theoretically ruin our compression ratio - +// left at zero to maximize install-time performance, because even a small minimum threshhold seems to +// have a high install-time performance cost for little or no size benefit. The value is left here for +// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB. +static const DWORD MINFLUSHTHRESHHOLD = 0; + +// structs +struct MS_CABINET_HEADER +{ + DWORD sig; + DWORD csumHeader; + DWORD cbCabinet; + DWORD csumFolders; + DWORD coffFiles; + DWORD csumFiles; + WORD version; + WORD cFolders; + WORD cFiles; + WORD flags; + WORD setID; + WORD iCabinet; +}; + + +struct MS_CABINET_ITEM +{ + DWORD cbFile; + DWORD uoffFolderStart; + WORD iFolder; + WORD date; + WORD time; + WORD attribs; +}; + +struct CABC_INTERNAL_ADDFILEINFO +{ + LPCWSTR wzSourcePath; + LPCWSTR wzEmptyPath; +}; + +struct CABC_DUPLICATEFILE +{ + DWORD dwFileArrayIndex; + DWORD dwDuplicateCabFileIndex; + LPWSTR pwzSourcePath; + LPWSTR pwzToken; +}; + + +struct CABC_FILE +{ + DWORD dwCabFileIndex; + LPWSTR pwzSourcePath; + LPWSTR pwzToken; + PMSIFILEHASHINFO pmfHash; + LONGLONG llFileSize; + BOOL fHasDuplicates; +}; + + +struct CABC_DATA +{ + LONGLONG llBytesSinceLastFlush; + LONGLONG llFlushThreshhold; + + STRINGDICT_HANDLE shDictHandle; + + WCHAR wzCabinetPath[MAX_PATH]; + WCHAR wzEmptyFile[MAX_PATH]; + HANDLE hEmptyFile; + DWORD dwLastFileIndex; + + DWORD cFilePaths; + DWORD cMaxFilePaths; + CABC_FILE *prgFiles; + + DWORD cDuplicates; + DWORD cMaxDuplicates; + CABC_DUPLICATEFILE *prgDuplicates; + + HRESULT hrLastError; + BOOL fGoodCab; + + HFCI hfci; + ERF erf; + CCAB ccab; + TCOMP tc; + + // Below Field are used for Cabinet Splitting + BOOL fCabinetSplittingEnabled; + FileSplitCabNamesCallback fileSplitCabNamesCallback; + WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting +}; + +const int CABC_HANDLE_BYTES = sizeof(CABC_DATA); + +// +// prototypes +// +static void FreeCabCData( + __in CABC_DATA* pcd + ); +static HRESULT CheckForDuplicateFile( + __in CABC_DATA *pcd, + __out CABC_FILE **ppcf, + __in LPCWSTR wzFileName, + __in PMSIFILEHASHINFO *ppmfHash, + __in LONGLONG llFileSize + ); +static HRESULT AddDuplicateFile( + __in CABC_DATA *pcd, + __in DWORD dwFileArrayIndex, + __in_z LPCWSTR wzSourcePath, + __in_opt LPCWSTR wzToken, + __in DWORD dwDuplicateCabFileIndex + ); +static HRESULT AddNonDuplicateFile( + __in CABC_DATA *pcd, + __in LPCWSTR wzFile, + __in_opt LPCWSTR wzToken, + __in_opt const MSIFILEHASHINFO* pmfHash, + __in LONGLONG llFileSize, + __in DWORD dwCabFileIndex + ); +static HRESULT UpdateDuplicateFiles( + __in const CABC_DATA *pcd + ); +static HRESULT DuplicateFile( + __in MS_CABINET_HEADER *pHeader, + __in const CABC_DATA *pcd, + __in const CABC_DUPLICATEFILE *pDuplicate + ); +static HRESULT UtcFileTimeToLocalDosDateTime( + __in const FILETIME* pFileTime, + __out USHORT* pDate, + __out USHORT* pTime + ); + +static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb); +static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv); +static __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); +static __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); +static __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); +static __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); +static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __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); +static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __inout_bcount(CABC_HANDLE_BYTES) void *pv); + + +/******************************************************************** +CabcBegin - begins creating a cabinet + +NOTE: phContext must be the same handle used in AddFile and Finish. + wzCabDir can be L"", but not NULL. + dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case. + +********************************************************************/ +extern "C" HRESULT DAPI CabCBegin( + __in_z LPCWSTR wzCab, + __in_z LPCWSTR wzCabDir, + __in DWORD dwMaxFiles, + __in DWORD dwMaxSize, + __in DWORD dwMaxThresh, + __in COMPRESSION_TYPE ct, + __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext + ) +{ + Assert(wzCab && *wzCab && phContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = NULL; + WCHAR wzTempPath[MAX_PATH] = { }; + + C_ASSERT(sizeof(MSIFILEHASHINFO) == 20); + + WCHAR wzPathBuffer [MAX_PATH] = L""; + size_t cchPathBuffer; + if (wzCabDir) + { + hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); + CabcExitOnFailure(hr, "Failed to get length of cab directory"); + + // Need room to terminate with L'\\' and L'\0' + if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer) + { + hr = E_INVALIDARG; + CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); + } + + hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir); + CabcExitOnFailure(hr, "Failed to copy cab directory to buffer"); + + if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) + { + hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\"); + CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer"); + ++cchPathBuffer; + } + } + + pcd = static_cast(MemAlloc(sizeof(CABC_DATA), TRUE)); + CabcExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure"); + + pcd->hrLastError = S_OK; + pcd->fGoodCab = TRUE; + pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD; + + pcd->hEmptyFile = INVALID_HANDLE_VALUE; + + pcd->fileSplitCabNamesCallback = NULL; + + if (NULL == dwMaxSize) + { + pcd->ccab.cb = CAB_MAX_SIZE; + pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired + } + else + { + pcd->ccab.cb = dwMaxSize * 1024 * 1024; + pcd->fCabinetSplittingEnabled = TRUE; + } + + if (0 == dwMaxThresh) + { + // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work. + pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16; + } + else + { + pcd->ccab.cbFolderThresh = dwMaxThresh; + } + + // Translate the compression type + if (COMPRESSION_TYPE_NONE == ct) + { + pcd->tc = tcompTYPE_NONE; + } + else if (COMPRESSION_TYPE_LOW == ct) + { + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO; + } + else if (COMPRESSION_TYPE_MEDIUM == ct) + { + pcd->tc = TCOMPfromLZXWindow(18); + } + else if (COMPRESSION_TYPE_HIGH == ct) + { + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI; + } + else if (COMPRESSION_TYPE_MSZIP == ct) + { + pcd->tc = tcompTYPE_MSZIP; + } + else + { + hr = E_INVALIDARG; + CabcExitOnFailure(hr, "Invalid compression type specified."); + } + + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL)) + { + CabcExitWithLastError(hr, "failed to convert cab name to multi-byte"); + } + + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) + { + CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte"); + } + + // Remember the path to the cabinet. + hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); + CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); + + hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab); + CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); + + // Get the empty file to use as the blank marker for duplicates. + if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + CabcExitWithLastError(hr, "Failed to get temp path."); + } + + if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile)) + { + CabcExitWithLastError(hr, "Failed to create a temp file name."); + } + + // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) + // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst + // case is we'll leave a zero byte file behind in the temp folder. + 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); + + hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); + CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); + + // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files + if (1 > dwMaxFiles) + { + dwMaxFiles = 1; + } + + pcd->cMaxFilePaths = dwMaxFiles; + size_t cbFileAllocSize = 0; + + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize)); + CabcExitOnFailure(hr, "Maximum allocation exceeded on initialization."); + + pcd->prgFiles = static_cast(MemAlloc(cbFileAllocSize, TRUE)); + CabcExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files."); + + // Tell cabinet API about our configuration. + pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd); + if (NULL == pcd->hfci || pcd->erf.fError) + { + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + CabcExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + + pcd->fGoodCab = FALSE; + + 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? + } + + *phContext = pcd; + +LExit: + if (FAILED(hr) && pcd && pcd->hfci) + { + ::FCIDestroy(pcd->hfci); + } + + return hr; +} + + +/******************************************************************** +CabCNextCab - This will be useful when creating multiple cabs. +Haven't needed it yet. +********************************************************************/ +extern "C" HRESULT DAPI CabCNextCab( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + UNREFERENCED_PARAMETER(hContext); + // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls + return E_NOTIMPL; +} + + +/******************************************************************** +CabcAddFile - adds a file to a cabinet + +NOTE: hContext must be the same used in Begin and Finish +if wzToken is null, the file's original name is used within the cab +********************************************************************/ +extern "C" HRESULT DAPI CabCAddFile( + __in_z LPCWSTR wzFile, + __in_z_opt LPCWSTR wzToken, + __in_opt PMSIFILEHASHINFO pmfHash, + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + Assert(wzFile && *wzFile && hContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = reinterpret_cast(hContext); + CABC_FILE *pcfDuplicate = NULL; + LONGLONG llFileSize = 0; + PMSIFILEHASHINFO pmfLocalHash = pmfHash; + + // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired + // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled + if(!pcd->fCabinetSplittingEnabled) + { + // Store file size, primarily used to determine which files to hash for duplicates + hr = FileSize(wzFile, &llFileSize); + CabcExitOnFailure(hr, "Failed to check size of file %ls", wzFile); + + hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize); + CabcExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); + } + + if (pcfDuplicate) // This will be null for smart cabbing case + { + DWORD index; + hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index); + CabcExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); + + hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex); + CabcExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); + } + else + { + hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); + CabcExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); + } + + ++pcd->dwLastFileIndex; + +LExit: + // If we allocated a hash struct ourselves, free it + if (pmfHash != pmfLocalHash) + { + ReleaseMem(pmfLocalHash); + } + + return hr; +} + + +/******************************************************************** +CabcFinish - finishes making a cabinet + +NOTE: hContext must be the same used in Begin and AddFile +*********************************************************************/ +extern "C" HRESULT DAPI CabCFinish( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback + ) +{ + Assert(hContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = reinterpret_cast(hContext); + CABC_INTERNAL_ADDFILEINFO fileInfo = { }; + DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex + DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array + DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array + LPSTR pszFileToken = NULL; + LONGLONG llFileSize = 0; + + pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback; + + // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile() + // doing so at appropriate times results in install-time performance benefits in the case of duplicate files. + // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump + // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder + // (this is not the same as a regular folder, and is a concept unique to CABs). + + // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files + // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that + // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB + // to close the current folder, and create a new folder for the next file to be added. + + // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence. + // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to") + // 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) + BOOL fFlushBefore = FALSE; + BOOL fFlushAfter = FALSE; + + ReleaseDict(pcd->shDictHandle); + + // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added + for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex) + { + if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file + { + // Just a normal, non-duplicated file. We'll add it to the list for later checking of + // duplicates. + fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath; + fileInfo.wzEmptyPath = NULL; + + // Use the provided token, otherwise default to the source file name. + if (pcd->prgFiles[dwArrayFileIndex].pwzToken) + { + LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken; + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp); + } + else + { + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp); + } + + if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates) + { + fFlushBefore = TRUE; + } + + llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize; + + ++dwArrayFileIndex; // Increment into the non-duplicate array + } + else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file + { + // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space + // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero + // byte files to point at their duplicated file index. + // + // Notice that duplicate files are not added to the list of file paths because all duplicate + // files point at the same path (the empty file) so there is no point in tracking them with + // their path. + fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath; + fileInfo.wzEmptyPath = pcd->wzEmptyFile; + + // Use the provided token, otherwise default to the source file name. + if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken) + { + LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken; + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp); + } + else + { + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp); + } + + // 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 + if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) && + !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) && + dwArrayFileIndex < pcd->cFilePaths) + { + fFlushAfter = TRUE; + } + + // We're just adding a 0-byte file, so set it appropriately + llFileSize = 0; + + ++dwDupeArrayFileIndex; // Increment into the duplicate array + } + else // If it's neither duplicate nor non-duplicate, throw an error + { + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); + CabcExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered"); + } + + if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) + { + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) + { + CabcExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + pcd->llBytesSinceLastFlush = 0; + } + + pcd->llBytesSinceLastFlush += llFileSize; + + // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct + // through the pointer to an ANSI string. This is neccessary so we can smuggle through the + // path to the empty file (should this be a duplicate file). +#pragma prefast(push) +#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here + if (!::FCIAddFile(pcd->hfci, reinterpret_cast(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc)) +#pragma prefast(pop) + { + pcd->fGoodCab = FALSE; + + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + 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); + } + + 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? + } + + // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet + // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead + if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + CabcExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet."); + } + + if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) + { + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) + { + CabcExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + pcd->llBytesSinceLastFlush = 0; + } + + fFlushAfter = FALSE; + fFlushBefore = FALSE; + } + + if (!pcd->fGoodCab) + { + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + CabcExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); + } + + 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? + } + + // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs) + if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus)) + { + // If we have a last error, use that, otherwise return the useless error + hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL; + 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? + } + + if (pcd->fGoodCab && pcd->cDuplicates) + { + hr = UpdateDuplicateFiles(pcd); + CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); + } + +LExit: + ::FCIDestroy(pcd->hfci); + FreeCabCData(pcd); + ReleaseNullStr(pszFileToken); + + return hr; +} + + +/******************************************************************** +CabCCancel - cancels making a cabinet + +NOTE: hContext must be the same used in Begin and AddFile +*********************************************************************/ +extern "C" void DAPI CabCCancel( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + Assert(hContext); + + CABC_DATA* pcd = reinterpret_cast(hContext); + ::FCIDestroy(pcd->hfci); + FreeCabCData(pcd); +} + + +// +// private +// + +static void FreeCabCData( + __in CABC_DATA* pcd + ) +{ + if (pcd) + { + ReleaseFileHandle(pcd->hEmptyFile); + + for (DWORD i = 0; i < pcd->cFilePaths; ++i) + { + ReleaseStr(pcd->prgFiles[i].pwzSourcePath); + ReleaseMem(pcd->prgFiles[i].pmfHash); + } + ReleaseMem(pcd->prgFiles); + ReleaseMem(pcd->prgDuplicates); + + ReleaseMem(pcd); + } +} + +/******************************************************************** + SmartCab functions + +********************************************************************/ + +static HRESULT CheckForDuplicateFile( + __in CABC_DATA *pcd, + __out CABC_FILE **ppcf, + __in LPCWSTR wzFileName, + __in PMSIFILEHASHINFO *ppmfHash, + __in LONGLONG llFileSize + ) +{ + DWORD i; + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + CabcExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file"); + CabcExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file"); + + *ppcf = NULL; // By default, we'll set our output to NULL + + hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast(ppcf)); + // If we found it in the hash of previously added source paths, return our match immediately + if (SUCCEEDED(hr)) + { + ExitFunction1(hr = S_OK); + } + else if (E_NOTFOUND == hr) + { + hr = S_OK; + } + CabcExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files"); + + for (i = 0; i < pcd->cFilePaths; ++i) + { + // If two files have the same size, use hashing to check if they're a match + if (llFileSize == pcd->prgFiles[i].llFileSize) + { + // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it + if (pcd->prgFiles[i].pmfHash == NULL) + { + pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + CabcExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash"); + + pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash); + CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath); + } + + // If our own file hasn't yet been hashed, hash it + if (NULL == *ppmfHash) + { + *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + CabcExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash"); + + (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash); + CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath); + } + + // If the two file hashes are both of the expected size, and they match, we've got a match, so return it! + if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize && + sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize && + pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] && + pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] && + pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] && + pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3]) + { + *ppcf = pcd->prgFiles + i; + ExitFunction1(hr = S_OK); + } + } + } + +LExit: + + return hr; +} + + +static HRESULT AddDuplicateFile( + __in CABC_DATA *pcd, + __in DWORD dwFileArrayIndex, + __in_z LPCWSTR wzSourcePath, + __in_opt LPCWSTR wzToken, + __in DWORD dwDuplicateCabFileIndex + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + + // Ensure there is enough memory to store this duplicate file index. + if (pcd->cDuplicates == pcd->cMaxDuplicates) + { + pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?) + size_t cbDuplicates = 0; + + hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates); + CabcExitOnFailure(hr, "Maximum allocation exceeded."); + + if (pcd->cDuplicates) + { + pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file."); + } + else + { + pv = MemAlloc(cbDuplicates, FALSE); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file."); + } + + ZeroMemory(reinterpret_cast(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE)); + + pcd->prgDuplicates = static_cast(pv); + pv = NULL; + } + + // Store the duplicate file index. + pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex; + pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex; + pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates + + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0); + CabcExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath); + + if (wzToken && *wzToken) + { + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0); + CabcExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken); + } + + ++pcd->cDuplicates; + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT AddNonDuplicateFile( + __in CABC_DATA *pcd, + __in LPCWSTR wzFile, + __in_opt LPCWSTR wzToken, + __in_opt const MSIFILEHASHINFO* pmfHash, + __in LONGLONG llFileSize, + __in DWORD dwCabFileIndex + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + + // Ensure there is enough memory to store this file index. + if (pcd->cFilePaths == pcd->cMaxFilePaths) + { + pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?) + size_t cbFilePaths = 0; + + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths); + CabcExitOnFailure(hr, "Maximum allocation exceeded."); + + pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file."); + + ZeroMemory(reinterpret_cast(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE)); + + pcd->prgFiles = static_cast(pv); + pv = NULL; + } + + // Store the file index information. + // TODO: add this to a sorted list so we can do a binary search later. + CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths; + pcf->dwCabFileIndex = dwCabFileIndex; + pcf->llFileSize = llFileSize; + + if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize) + { + pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + CabcExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash"); + + pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + pcf->pmfHash->dwData[0] = pmfHash->dwData[0]; + pcf->pmfHash->dwData[1] = pmfHash->dwData[1]; + pcf->pmfHash->dwData[2] = pmfHash->dwData[2]; + pcf->pmfHash->dwData[3] = pmfHash->dwData[3]; + } + + hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0); + CabcExitOnFailure(hr, "Failed to copy file path: %ls", wzFile); + + if (wzToken && *wzToken) + { + hr = StrAllocString(&pcf->pwzToken, wzToken, 0); + CabcExitOnFailure(hr, "Failed to copy file token: %ls", wzToken); + } + + ++pcd->cFilePaths; + + hr = DictAddValue(pcd->shDictHandle, pcf); + CabcExitOnFailure(hr, "Failed to add file to dictionary of added files"); + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT UpdateDuplicateFiles( + __in const CABC_DATA *pcd + ) +{ + HRESULT hr = S_OK; + DWORD cbCabinet = 0; + LARGE_INTEGER liCabinetSize = { }; + HANDLE hCabinet = INVALID_HANDLE_VALUE; + HANDLE hCabinetMapping = NULL; + LPVOID pv = NULL; + MS_CABINET_HEADER *pCabinetHeader = NULL; + + hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hCabinet) + { + CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); + } + + // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as + // the upper bound for the memory map. + if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) + { + CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); + } + + if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) + { + cbCabinet = liCabinetSize.LowPart; + } + else + { + cbCabinet = MAX_CABINET_HEADER_SIZE; + } + + // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE + hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); + if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) + { + CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); + } + + pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); + CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); + + pCabinetHeader = static_cast(pv); + + for (DWORD i = 0; i < pcd->cDuplicates; ++i) + { + const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i; + + hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile); + CabcExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex); + } + +LExit: + if (pv) + { + ::UnmapViewOfFile(pv); + } + if (hCabinetMapping) + { + ::CloseHandle(hCabinetMapping); + } + ReleaseFileHandle(hCabinet); + + return hr; +} + + +static HRESULT DuplicateFile( + __in MS_CABINET_HEADER *pHeader, + __in const CABC_DATA *pcd, + __in const CABC_DUPLICATEFILE *pDuplicate + ) +{ + HRESULT hr = S_OK; + BYTE *pbHeader = reinterpret_cast(pHeader); + BYTE* pbItem = pbHeader + pHeader->coffFiles; + const MS_CABINET_ITEM *pOriginalItem = NULL; + MS_CABINET_ITEM *pDuplicateItem = NULL; + + if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex || + pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex || + pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex) + { + hr = E_UNEXPECTED; + CabcExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex); + } + + // Step through each cabinet items until we get to the original + // file's index. Notice that the name of the cabinet item is + // appended to the end of the MS_CABINET_INFO, that's why we can't + // index straight to the data we want. + for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i) + { + LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; + } + + pOriginalItem = reinterpret_cast(pbItem); + + // Now pick up where we left off after the original file's index + // was found and loop until we find the duplicate file's index. + for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i) + { + LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; + } + + pDuplicateItem = reinterpret_cast(pbItem); + + if (0 != pDuplicateItem->cbFile) + { + hr = E_UNEXPECTED; + CabcExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile); + } + + pDuplicateItem->cbFile = pOriginalItem->cbFile; + pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart; + pDuplicateItem->iFolder = pOriginalItem->iFolder; + // Note: we do *not* duplicate the date/time and attributes metadata from + // the original item to the duplicate. The following lines are commented + // so people are not tempted to put them back. + //pDuplicateItem->date = pOriginalItem->date; + //pDuplicateItem->time = pOriginalItem->time; + //pDuplicateItem->attribs = pOriginalItem->attribs; + +LExit: + return hr; +} + + +static HRESULT UtcFileTimeToLocalDosDateTime( + __in const FILETIME* pFileTime, + __out USHORT* pDate, + __out USHORT* pTime + ) +{ + HRESULT hr = S_OK; + FILETIME ftLocal = { }; + + if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal)) + { + CabcExitWithLastError(hr, "Filed to convert file time to local file time."); + } + + if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime)) + { + CabcExitWithLastError(hr, "Filed to convert file time to DOS date time."); + } + +LExit: + return hr; +} + + +/******************************************************************** + FCI callback functions + +*********************************************************************/ +static __callback int DIAMONDAPI CabCFilePlaced( + __in PCCAB pccab, + __in_z PSTR szFile, + __in long cbFile, + __in BOOL fContinuation, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(pccab); + UNREFERENCED_PARAMETER(szFile); + UNREFERENCED_PARAMETER(cbFile); + UNREFERENCED_PARAMETER(fContinuation); + UNREFERENCED_PARAMETER(pv); + return 0; +} + + +static __callback void * DIAMONDAPI CabCAlloc( + __in ULONG cb + ) +{ + return MemAlloc(cb, FALSE); +} + + +static __callback void DIAMONDAPI CabCFree( + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + MemFree(pv); +} + +static __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 + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + INT_PTR pFile = -1; + DWORD dwAccess = 0; + DWORD dwDisposition = 0; + DWORD dwAttributes = 0; + + // + // Translate flags for CreateFile + // + if (oflag & _O_CREAT) + { + if (pmode == _S_IREAD) + dwAccess |= GENERIC_READ; + else if (pmode == _S_IWRITE) + dwAccess |= GENERIC_WRITE; + else if (pmode == (_S_IWRITE | _S_IREAD)) + dwAccess |= GENERIC_READ | GENERIC_WRITE; + + if (oflag & _O_SHORT_LIVED) + dwDisposition = FILE_ATTRIBUTE_TEMPORARY; + else if (oflag & _O_TEMPORARY) + dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE; + else if (oflag & _O_EXCL) + dwDisposition = CREATE_NEW; + } + if (oflag & _O_TRUNC) + dwDisposition = CREATE_ALWAYS; + + if (!dwAccess) + dwAccess = GENERIC_READ; + if (!dwDisposition) + dwDisposition = OPEN_EXISTING; + if (!dwAttributes) + dwAttributes = FILE_ATTRIBUTE_NORMAL; + + // Check to see if we were passed the magic character that says 'Unicode string follows'. + if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile) + { + pFile = reinterpret_cast(::CreateFileW(reinterpret_cast(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); + } + else + { +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + pFile = reinterpret_cast(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); +#pragma prefast(pop) + } + + if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) + { + CabcExitOnLastError(hr, "failed to open file: %s", pszFile); + } + +LExit: + if (FAILED(hr)) + pcd->hrLastError = *err = hr; + + return FAILED(hr) ? -1 : pFile; +} + + +static __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 + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD cbRead = 0; + + CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided"); + if (!::ReadFile(reinterpret_cast(hf), memory, cb, &cbRead, NULL)) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to read during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : cbRead; +} + + +static __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 + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided"); + if (!::WriteFile(reinterpret_cast(hf), memory, cb, &cbWrite, NULL)) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to write during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + pcd->hrLastError = *err = hr; + + return FAILED(hr) ? -1 : cbWrite; +} + + +static __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 + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + CabcExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype); + } + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + // Todo: update these comments for FCI (are they accurate for FCI as well?) + lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); + if (DWORD_MAX == lMove) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to move file pointer %d bytes", dist); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : lMove; +} + + +static __callback int FAR DIAMONDAPI CabCClose( + __in INT_PTR hf, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + + if (!::CloseHandle(reinterpret_cast(hf))) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to close file during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : 0; +} + +static __callback int DIAMONDAPI CabCDelete( + __in_z PSTR szFile, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(err); + UNREFERENCED_PARAMETER(pv); + +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + ::DeleteFileA(szFile); +#pragma prefast(pop) + + return 0; +} + + +__success(return != FALSE) +static __callback BOOL DIAMONDAPI CabCGetTempFile( + __out_bcount_z(cbFile) char *szFile, + __in int cbFile, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + static volatile DWORD dwIndex = 0; + + HRESULT hr = S_OK; + char szTempPath[MAX_PATH] = { }; + DWORD cchTempPath = MAX_PATH; + DWORD dwProcessId = ::GetCurrentProcessId(); + HANDLE hTempFile = INVALID_HANDLE_VALUE; + + if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) + { + CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation."); + } + + for (DWORD i = 0; i < DWORD_MAX; ++i) + { + LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast(&dwIndex)); + + hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); + CabcExitOnFailure(hr, "failed to format log file path."); + + hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (INVALID_HANDLE_VALUE != hTempFile) + { + // we found one that doesn't exist + hr = S_OK; + break; + } + else + { + hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. + } + } + CabcExitOnFailure(hr, "failed to find temporary file."); + +LExit: + ReleaseFileHandle(hTempFile); + + if (FAILED(hr)) + { + pcd->hrLastError = hr; + } + + return FAILED(hr)? FALSE : TRUE; +} + + +__success(return != FALSE) +static __callback BOOL DIAMONDAPI CabCGetNextCabinet( + __in PCCAB pccab, + __in ULONG ul, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(ul); + + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + LPWSTR pwzFileToken = NULL; + WCHAR wzNewCabName[MAX_PATH] = L""; + + if (pccab->iCab == 1) + { + pcd->wzFirstCabinetName[0] = '\0'; + LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath); + size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName)); + if (len > 4) + { + len -= 4; // remove Extention ".cab" of 8.3 Format + } + hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len); + CabcExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name"); + } + + const int nAlphabets = 26; // Number of Alphabets from a to z + if (pccab->iCab <= nAlphabets) + { + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + } + else if (pccab->iCab <= nAlphabets*nAlphabets) + { + // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ...... + int char2 = (pccab->iCab) % nAlphabets; + int char1 = (pccab->iCab - char2)/nAlphabets; + if (char2 == 0) + { + // e.g. when iCab = 52, we want az + char2 = nAlphabets; // Second char must be 'z' in this case + char1--; // First Char must be decremented by 1 + } + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + } + else + { + hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index. + CabcExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name"); + } + + // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method + if(pcd->fileSplitCabNamesCallback != 0) + { + // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split + // This code will need updation if we need to send all file tokens for the splitting Cabinets + if (pcd->prgFiles[0].pwzToken) + { + pwzFileToken = pcd->prgFiles[0].pwzToken; + } + else + { + LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath; + pwzFileToken = FileFromPath(wzSourcePath); + } + + // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table + pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken); + } + +LExit: + if (FAILED(hr)) + { + // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!! + // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting + pcd->hrLastError = hr; + return FALSE; + } + else + { + return TRUE; + } +} + + +static __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 + ) +{ + HRESULT hr = S_OK; + CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast(pszName); + LPCWSTR wzFile = NULL; + DWORD cbFile = 0; + LPSTR pszFilePlusMagic = NULL; + DWORD cbFilePlusMagic = 0; + WIN32_FILE_ATTRIBUTE_DATA fad = { }; + INT_PTR iResult = -1; + + // If there is an empty file provided, use that as the source path to cab (since we + // must be dealing with a duplicate file). Otherwise, use the source path you'd expect. + wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath; + cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR); + + // Convert the source file path into an Ansi string that our APIs will recognize as + // a Unicode string (due to the magic character). + cbFilePlusMagic = cbFile + 1; // add one for the magic. + pszFilePlusMagic = reinterpret_cast(MemAlloc(cbFilePlusMagic, TRUE)); + + *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER; + memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile); + + if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad)) + { + CabcExitWithLastError(hr, "Failed to get file attributes on '%ls'.", pFileInfo->wzSourcePath); + } + + // Set the attributes but only allow the few attributes that CAB supports. + *pattribs = static_cast(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE); + + hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime); + if (FAILED(hr)) + { + // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were + // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't + // found. This would create further problems if the file was written to the CAB without this value. Windows + // Installer would then fail to extract the file. + hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime); + CabcExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName); + } + + iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv); + +LExit: + ReleaseMem(pszFilePlusMagic); + if (FAILED(hr)) + { + *err = (int)hr; + } + + return FAILED(hr) ? -1 : iResult; +} + + +static __callback long DIAMONDAPI CabCStatus( + __in UINT ui, + __in ULONG cb1, + __in ULONG cb2, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(ui); + UNREFERENCED_PARAMETER(cb1); + UNREFERENCED_PARAMETER(cb2); + UNREFERENCED_PARAMETER(pv); + return 0; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CabExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) +#define CabExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) +#define CabExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) +#define CabExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) +#define CabExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABUTIL, e, x, s, __VA_ARGS__) +#define CabExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABUTIL, g, x, s, __VA_ARGS__) + + +// external prototypes +typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*); +typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); +typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO); +typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *); + + +// +// static globals +// +static HMODULE vhCabinetDll = NULL; + +static HFDI vhfdi = NULL; +static PFNFDICREATE vpfnFDICreate = NULL; +static PFNFDICOPY vpfnFDICopy = NULL; +static PFNFDIISCABINET vpfnFDIIsCabinet = NULL; +static PFNFDIDESTROY vpfnFDIDestroy = NULL; +static ERF verf; + +static DWORD64 vdw64EmbeddedOffset = 0; + +// +// structs +// +struct CAB_CALLBACK_STRUCT +{ + BOOL fStopExtracting; // flag set when no more files are needed + LPCWSTR pwzExtract; // file to extract ("*" means extract all) + LPCWSTR pwzExtractDir; // directory to extract files to + + // possible user data + CAB_CALLBACK_PROGRESS pfnProgress; + LPVOID pvContext; +}; + +// +// prototypes +// +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize); +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData); +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode); +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb); +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb); +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf); +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype); +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify); +static 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); + +static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL; + + +inline HRESULT LoadCabinetDll() +{ + HRESULT hr = S_OK; + if (!vhCabinetDll) + { + hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll); + CabExitOnFailure(hr, "failed to load cabinet.dll"); + + // retrieve all address functions + vpfnFDICreate = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICreate")); + CabExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL"); + vpfnFDICopy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICopy")); + CabExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL"); + vpfnFDIIsCabinet = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIIsCabinet")); + CabExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL"); + vpfnFDIDestroy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIDestroy")); + CabExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL"); + + vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf); + CabExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll"); + } + +LExit: + if (FAILED(hr) && vhCabinetDll) + { + ::FreeLibrary(vhCabinetDll); + vhCabinetDll = NULL; + } + + return hr; +} + + +static HANDLE OpenFileWithRetry( + __in LPCWSTR wzPath, + __in DWORD dwDesiredAccess, + __in DWORD dwCreationDisposition +) +{ + HANDLE hFile = INVALID_HANDLE_VALUE; + + for (DWORD i = 0; i < 30; ++i) + { + hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hFile) + { + break; + } + + ::Sleep(100); + } + + return hFile; +} + + +/******************************************************************** + CabInitialize - initializes internal static variables + +********************************************************************/ +extern "C" HRESULT DAPI CabInitialize( + __in BOOL fDelayLoad + ) +{ + HRESULT hr = S_OK; + + if (!fDelayLoad) + { + hr = LoadCabinetDll(); + CabExitOnFailure(hr, "failed to load CABINET.DLL"); + } + +LExit: + return hr; +} + + +/******************************************************************** + CabUninitialize - initializes internal static variables + +********************************************************************/ +extern "C" void DAPI CabUninitialize( + ) +{ + if (vhfdi) + { + if (vpfnFDIDestroy) + { + vpfnFDIDestroy(vhfdi); + } + vhfdi = NULL; + } + + vpfnFDICreate = NULL; + vpfnFDICopy =NULL; + vpfnFDIIsCabinet = NULL; + vpfnFDIDestroy = NULL; + + if (vhCabinetDll) + { + ::FreeLibrary(vhCabinetDll); + vhCabinetDll = NULL; + } +} + +/******************************************************************** + CabEnumerate - list files inside cabinet + + NOTE: wzCabinet must be full path to cabinet file + pfnNotify is callback function to get notified for each file + in the cabinet +********************************************************************/ +extern "C" HRESULT DAPI CabEnumerate( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzEnumerateFile, + __in STDCALL_PFNFDINOTIFY pfnNotify, + __in DWORD64 dw64EmbeddedOffset + ) +{ + return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset); +} + +/******************************************************************** + CabExtract - extracts one or all files from a cabinet + + NOTE: wzCabinet must be full path to cabinet file + wzExtractFile can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa +********************************************************************/ +extern "C" HRESULT DAPI CabExtract( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzExtractFile, + __in_z LPCWSTR wzExtractDir, + __in_opt CAB_CALLBACK_PROGRESS pfnProgress, + __in_opt LPVOID pvContext, + __in DWORD64 dw64EmbeddedOffset + ) +{ + return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset); +} + +// +// private +// +/******************************************************************** + FDINotify -- wrapper that converts call convention from __cdecl to __stdcall. + + NOTE: Since netfx 1.1 supports only function pointers (delegates) + with __stdcall calling convention and cabinet api uses + __cdecl calling convention, we need this wrapper function. + netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate. + TODO: remove this when upgrading to netfx 2.0. +********************************************************************/ +static __callback INT_PTR DIAMONDAPI FDINotify( + __in FDINOTIFICATIONTYPE iNotification, + __inout FDINOTIFICATION *pFDINotify + ) +{ + if (NULL != v_pfnNetFx11Notify) + { + return v_pfnNetFx11Notify(iNotification, pFDINotify); + } + else + { + return (INT_PTR)0; + } +} + + +/******************************************************************** + CabOperation - helper function that enumerates or extracts files + from cabinet + + NOTE: wzCabinet must be full path to cabinet file + wzExtractFile can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa + pfnNotify is callback function to get notified for each file + in the cabinet. If it's NULL, files will be extracted. +********************************************************************/ +static 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 + ) +{ + HRESULT hr = S_OK; + BOOL fResult; + + LPWSTR sczCabinet = NULL; + LPWSTR pwz = NULL; + CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings + CHAR szCabFile[MAX_PATH * 4]; + + CAB_CALLBACK_STRUCT ccs; + PFNFDINOTIFY pfnFdiNotify; + + // + // ensure the cabinet.dll is loaded + // + if (!vhfdi) + { + hr = LoadCabinetDll(); + CabExitOnFailure(hr, "failed to load CABINET.DLL"); + } + + hr = StrAllocString(&sczCabinet, wzCabinet, 0); + CabExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet); + + // + // split the cabinet full path into directory and filename and convert to multi-byte (ick!) + // + pwz = FileFromPath(sczCabinet); + CabExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet); + + if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL)) + { + CabExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz); + } + + *pwz = '\0'; + + // If a full path was not provided, use the relative current directory. + if (wzCabinet == pwz) + { + hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); + CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); + } + else + { + if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) + { + CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); + } + } + + // + // iterate through files in cabinet extracting them to the callback function + // + ccs.fStopExtracting = FALSE; + ccs.pwzExtract = wzExtractFile; + ccs.pwzExtractDir = wzExtractDir; + ccs.pfnProgress = pfnProgress; + ccs.pvContext = pvContext; + + vdw64EmbeddedOffset = dw64EmbeddedOffset; + + // if pfnNotify is given, use it, otherwise use default callback + if (NULL == pfnNotify) + { + pfnFdiNotify = CabExtractCallback; + } + else + { + v_pfnNetFx11Notify = pfnNotify; + pfnFdiNotify = FDINotify; + } + fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); + if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure + { + CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); + } + +LExit: + ReleaseStr(sczCabinet); + v_pfnNetFx11Notify = NULL; + + return hr; +} + +/**************************************************************************** + default extract routines + +****************************************************************************/ +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize) +{ + return MemAlloc(dwSize, FALSE); +} + + +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData) +{ + MemFree(pvData); +} + + +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + INT_PTR pFile = -1; + LPWSTR sczCabFile = NULL; + + // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) + { + hr = E_OUTOFMEMORY; + CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); + } + + hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); + CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); + + hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING); + if (INVALID_HANDLE_VALUE == hFile) + { + CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile); + } + + pFile = reinterpret_cast(hFile); + + if (vdw64EmbeddedOffset) + { + hr = CabExtractSeek(pFile, 0, 0); + CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); + } + + hFile = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hFile); + ReleaseStr(sczCabFile); + + return FAILED(hr) ? -1 : pFile; +} + + +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb) +{ + HRESULT hr = S_OK; + DWORD cbRead = 0; + + CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read"); + if (!::ReadFile(reinterpret_cast(hf), pv, cb, &cbRead, NULL)) + { + CabExitWithLastError(hr, "failed to read during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb) +{ + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write"); + if (!::WriteFile(reinterpret_cast(hf), pv, cb, &cbWrite, NULL)) + { + CabExitWithLastError(hr, "failed to write during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype) +{ + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + dist += static_cast(vdw64EmbeddedOffset); + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + } + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); + if (0xFFFFFFFF == lMove) + { + CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + } + +LExit: + return FAILED(hr) ? -1 : lMove - static_cast(vdw64EmbeddedOffset); +} + + +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf) +{ + HRESULT hr = S_OK; + + if (!::CloseHandle(reinterpret_cast(hf))) + { + CabExitWithLastError(hr, "failed to close file during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : 0; +} + + +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify) +{ + Assert(pFDINotify->pv); + + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + INT_PTR ipResult = 0; // result to return on success + + CAB_CALLBACK_STRUCT* pccs = static_cast(pFDINotify->pv); + LPCSTR sz; + WCHAR wz[MAX_PATH]; + FILETIME ft; + + switch (iNotification) + { + case fdintCOPY_FILE: // begin extracting a resource from cabinet + CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + + if (pccs->fStopExtracting) + { + ExitFunction1(hr = S_FALSE); // no more extracting + } + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (pccs->pfnProgress) + { + hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); + if (S_OK != hr) + { + ExitFunction(); + } + } + + if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) + { + // get the created date for the resource in the cabinet + FILETIME ftLocal; + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) + { + CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); + } + ::LocalFileTimeToFileTime(&ftLocal, &ft); + + WCHAR wzPath[MAX_PATH]; + hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); + CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); + hr = ::StringCchCatW(wzPath, countof(wzPath), wz); + CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + + hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS); + if (INVALID_HANDLE_VALUE == hFile) + { + CabExitWithLastError(hr, "failed to create file: %ls", wzPath); + } + + ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) + + if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) + { + if (::SetEndOfFile(hFile)) + { + ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer + } + } + + ipResult = reinterpret_cast(hFile); + hFile = INVALID_HANDLE_VALUE; + } + else // resource wasn't requested, skip it + { + hr = S_OK; + ipResult = 0; + } + + break; + case fdintCLOSE_FILE_INFO: // resource extraction complete + Assert(pFDINotify->hf && pFDINotify->psz1); + CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); + + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (NULL != pFDINotify->hf) // just close the file + { + ::CloseHandle(reinterpret_cast(pFDINotify->hf)); + } + + if (pccs->pfnProgress) + { + hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); + } + + if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going + { + ipResult = TRUE; + } + else // something went wrong or we only needed to extract one file + { + hr = S_OK; + ipResult = FALSE; + pccs->fStopExtracting = TRUE; + } + + break; + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + default: + AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); + }; + +LExit: + ReleaseFileHandle(hFile); + + return (S_OK == hr) ? ipResult : -1; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CertExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) +#define CertExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) +#define CertExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) +#define CertExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) +#define CertExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CERTUTIL, e, x, s, __VA_ARGS__) +#define CertExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CERTUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +CertReadProperty - reads a property from the certificate. + +NOTE: call MemFree() on the returned pvValue. +********************************************************************/ +extern "C" HRESULT DAPI CertReadProperty( + __in PCCERT_CONTEXT pCertContext, + __in DWORD dwProperty, + __deref_out_bound LPVOID* ppvValue, + __out_opt DWORD* pcbValue + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb)) + { + CertExitWithLastError(hr, "Failed to get size of certificate property."); + } + + pv = MemAlloc(cb, TRUE); + CertExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property."); + + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb)) + { + CertExitWithLastError(hr, "Failed to get certificate property."); + } + + *ppvValue = pv; + pv = NULL; + + if (pcbValue) + { + *pcbValue = cb; + } + +LExit: + ReleaseMem(pv); + return hr; +} + + +extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp( + __in CMSG_SIGNER_INFO* pSignerInfo, + __out FILETIME* pftSigningTimestamp + ) +{ + HRESULT hr = S_OK; + CRYPT_INTEGER_BLOB* pBlob = NULL; + PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; + DWORD cbSigningTimestamp = sizeof(FILETIME); + + // Find the countersigner blob. The countersigner in Authenticode contains the time + // that signing took place. It's a "countersigner" because the signing time was sent + // off to the certificate authority in the sky to return the verified time signed. + for (DWORD i = 0; i < pSignerInfo->UnauthAttrs.cAttr; ++i) + { + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_counterSign, -1, pSignerInfo->UnauthAttrs.rgAttr[i].pszObjId, -1)) + { + pBlob = pSignerInfo->UnauthAttrs.rgAttr[i].rgValue; + break; + } + } + + if (!pBlob) + { + hr = TRUST_E_FAIL; + CertExitOnFailure(hr, "Failed to find countersigner in signer information."); + } + + hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast(&pCounterSignerInfo), NULL); + CertExitOnFailure(hr, "Failed to decode countersigner information."); + + pBlob = NULL; // reset the blob before searching for the signing time. + + // Find the signing time blob in the countersigner. + for (DWORD i = 0; i < pCounterSignerInfo->AuthAttrs.cAttr; ++i) + { + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_signingTime, -1, pCounterSignerInfo->AuthAttrs.rgAttr[i].pszObjId, -1)) + { + pBlob = pCounterSignerInfo->AuthAttrs.rgAttr[i].rgValue; + break; + } + } + + if (!pBlob) + { + hr = TRUST_E_FAIL; + CertExitOnFailure(hr, "Failed to find signing time in countersigner information."); + } + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp)) + { + CertExitWithLastError(hr, "Failed to decode countersigner signing timestamp."); + } + +LExit: + ReleaseMem(pCounterSignerInfo); + + return hr; +} + + +extern "C" HRESULT DAPI GetCryptProvFromCert( + __in_opt HWND hwnd, + __in PCCERT_CONTEXT pCert, + __out HCRYPTPROV *phCryptProv, + __out DWORD *pdwKeySpec, + __in BOOL *pfDidCryptAcquire, + __deref_opt_out LPWSTR *ppwszTmpContainer, + __deref_opt_out LPWSTR *ppwszProviderName, + __out DWORD *pdwProviderType + ) +{ + HRESULT hr = S_OK; + HMODULE hMsSign32 = NULL; + + typedef BOOL (WINAPI *GETCRYPTPROVFROMCERTPTR)(HWND, PCCERT_CONTEXT, HCRYPTPROV*, DWORD*,BOOL*,LPWSTR*,LPWSTR*,DWORD*); + GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL; + + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); + CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + + pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert"); + CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + + if (!pGetCryptProvFromCert(hwnd, + pCert, + phCryptProv, + pdwKeySpec, + pfDidCryptAcquire, + ppwszTmpContainer, + ppwszProviderName, + pdwProviderType)) + { + CertExitWithLastError(hr, "Failed to get CSP from cert."); + } +LExit: + return hr; +} + +extern "C" HRESULT DAPI FreeCryptProvFromCert( + __in BOOL fAcquired, + __in HCRYPTPROV hProv, + __in_opt LPWSTR pwszCapiProvider, + __in DWORD dwProviderType, + __in_opt LPWSTR pwszTmpContainer + ) +{ + HRESULT hr = S_OK; + HMODULE hMsSign32 = NULL; + + typedef void (WINAPI *FREECRYPTPROVFROMCERT)(BOOL, HCRYPTPROV, LPWSTR, DWORD, LPWSTR); + FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL; + + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); + CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + + pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert"); + CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + + pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer); +LExit: + return hr; +} + +extern "C" HRESULT DAPI GetProvSecurityDesc( + __in HCRYPTPROV hProv, + __deref_out SECURITY_DESCRIPTOR** ppSecurity) +{ + HRESULT hr = S_OK; + ULONG ulSize = 0; + SECURITY_DESCRIPTOR* pSecurity = NULL; + + // Get the size of the security descriptor. + if (!::CryptGetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + NULL, + &ulSize, + DACL_SECURITY_INFORMATION)) + { + CertExitWithLastError(hr, "Error getting security descriptor size for CSP."); + } + + // Allocate the memory for the security descriptor. + pSecurity = static_cast(MemAlloc(ulSize, TRUE)); + CertExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL"); + + // Get the security descriptor. + if (!::CryptGetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + (BYTE*)pSecurity, + &ulSize, + DACL_SECURITY_INFORMATION)) + { + MemFree(pSecurity); + CertExitWithLastError(hr, "Error getting security descriptor for CSP."); + } + *ppSecurity = pSecurity; + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI SetProvSecurityDesc( + __in HCRYPTPROV hProv, + __in SECURITY_DESCRIPTOR* pSecurity) +{ + HRESULT hr = S_OK; + + // Set the new security descriptor. + if (!::CryptSetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + (BYTE*)pSecurity, + DACL_SECURITY_INFORMATION)) + { + CertExitWithLastError(hr, "Error setting security descriptor for CSP."); + } +LExit: + return hr; +} + +extern "C" BOOL DAPI CertHasPrivateKey( + __in PCCERT_CONTEXT pCertContext, + __out_opt DWORD* pdwKeySpec) +{ + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey = NULL; + DWORD dwKeySpec = 0; + // set CRYPT_ACQUIRE_CACHE_FLAG so that we don't have to release the private key handle + BOOL fResult = ::CryptAcquireCertificatePrivateKey( + pCertContext, + CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG, + 0, //pvReserved + &hPrivateKey, + &dwKeySpec, + NULL + ); + if (pdwKeySpec) + { + *pdwKeySpec = dwKeySpec; + } + return fResult; +} + + +extern "C" HRESULT DAPI CertInstallSingleCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + CERT_BLOB blob = { }; + + DWORD dwKeySpec = 0; + + HCRYPTPROV hCsp = NULL; + LPWSTR pwszTmpContainer = NULL; + LPWSTR pwszProviderName = NULL; + DWORD dwProviderType = 0; + BOOL fAcquired = TRUE; + + SECURITY_DESCRIPTOR* pSecurity = NULL; + SECURITY_DESCRIPTOR* pSecurityNew = NULL; + + // Update the friendly name of the certificate to be configured. + blob.pbData = (BYTE*)wzName; + blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null + + if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob)) + { + CertExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName); + } + + if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) + { + CertExitWithLastError(hr, "Failed to add certificate to the store."); + } + + // if the certificate has a private key, grant Administrators access + if (CertHasPrivateKey(pCertContext, &dwKeySpec)) + { + if (AT_KEYEXCHANGE == dwKeySpec || AT_SIGNATURE == dwKeySpec) + { + // We added a CSP key + hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType); + CertExitOnFailure(hr, "Failed to get handle to CSP"); + + hr = GetProvSecurityDesc(hCsp, &pSecurity); + CertExitOnFailure(hr, "Failed to get security descriptor of CSP"); + + hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew); + CertExitOnFailure(hr, "Failed to create new security descriptor"); + + hr = SetProvSecurityDesc(hCsp, pSecurityNew); + CertExitOnFailure(hr, "Failed to set Admin ACL on CSP"); + } + + if (CERT_NCRYPT_KEY_SPEC == dwKeySpec) + { + // We added a CNG key + // TODO change ACL on CNG key + } + } +LExit: + if (hCsp) + { + FreeCryptProvFromCert(fAcquired, hCsp, NULL, dwProviderType, NULL); + } + + ReleaseMem(pSecurity); + + if (pSecurityNew) + { + AclFreeSecurityDescriptor(pSecurityNew); + } + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__) +#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__) + + +static HANDLE vhStdIn = INVALID_HANDLE_VALUE; +static HANDLE vhStdOut = INVALID_HANDLE_VALUE; +static BOOL vfConsoleIn = FALSE; +static BOOL vfConsoleOut = FALSE; +static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; + + +extern "C" HRESULT DAPI ConsoleInitialize() +{ + Assert(INVALID_HANDLE_VALUE == vhStdOut); + HRESULT hr = S_OK; + UINT er; + + vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdIn) + { + ConExitOnLastError(hr, "failed to open stdin"); + } + + vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdOut) + { + ConExitOnLastError(hr, "failed to open stdout"); + } + + // check if we have a std in on the console + if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo)) + { + vfConsoleIn = TRUE; + } + else + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleIn= FALSE; + hr = S_OK; + } + else + { + ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); + } + } + + if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo)) + { + vfConsoleOut = TRUE; + } + else // no console + { + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleOut = FALSE; + hr = S_OK; + } + else + { + ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); + } + } + +LExit: + if (FAILED(hr)) + { + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; + } + + return hr; +} + + +extern "C" void DAPI ConsoleUninitialize() +{ + BOOL fOutEqualsIn = vhStdOut == vhStdIn; + + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; +} + + +extern "C" void DAPI ConsoleGreen() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleRed() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleYellow() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleNormal() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes); + } +} + + +/******************************************************************** + ConsoleWrite - full color printfA without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWrite( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + { + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + } + + cbTotal += cbWrote; + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteLine - full color printfA plus newline without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWriteLine( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + LPCSTR szNewLine = "\r\n"; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + // + // write the string + // + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + + cbTotal += cbWrote; + } + + // + // write the newline + // + if (!::WriteFile(vhStdOut, reinterpret_cast(szNewLine), 2, &cbWrote, NULL)) + { + ConExitOnLastError(hr, "failed to write newline to console"); + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteError - display an error to the screen + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d") +********************************************************************/ +HRESULT ConsoleWriteError( + HRESULT hrError, + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + LPSTR pszMessage = NULL; + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); + + if (FAILED(hrError)) + { + hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage); + } + else + { + hr = ConsoleWriteLine(cc, "Error: %s", pszMessage); + } + +LExit: + ReleaseStr(pszMessage); + return hr; +} + + +/******************************************************************** + ConsoleReadW - get console input without libc + + NOTE: only supports reading ANSI characters +********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadW( + __deref_out_z LPWSTR* ppwzBuffer + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + Assert(ppwzBuffer); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + DWORD cch = 0; + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + cch = 64; + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + + // loop until we read the \r\n from the console + for (;;) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + ConExitOnLastError(hr, "failed to read string from console"); + + cchTotalRead += cchRead; + if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1]) + { + psz[cchTotalRead - 2] = '\0'; // chop off the \r\n + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP); + +LExit: + ReleaseStr(psz); + return hr; +} + + +/******************************************************************** + ConsoleReadNonBlockingW - Read from the console without blocking + Won't work for redirected files (exe < txtfile), but will work for stdin redirected to + an anonymous or named pipe + + if (fReadLine), stop reading immediately when \r\n is found +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadNonBlockingW( + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, + __out DWORD* pcchSize, + BOOL fReadLine + ) +{ + Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize); + HRESULT hr = S_OK; + + LPSTR psz = NULL; + + ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); + + DWORD dwRead; + DWORD dwNumInput; + + DWORD cchTotal = 0; + DWORD cch = 8; + + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + DWORD dwIndex = 0; + DWORD er; + + INPUT_RECORD ir; + WCHAR chIn; + + *ppwzBuffer = NULL; + *pcchSize = 0; + + // If we really have a handle to stdin, and not the end of a pipe + if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL)) + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE != er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + + if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead)) + { + ConExitOnLastError(hr, "failed to peek at console input"); + } + + if (0 == dwRead) + { + ExitFunction1(hr = S_FALSE); + } + + for (/* dwRead from num of input events */; dwRead > 0; dwRead--) + { + if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput)) + { + ConExitOnLastError(hr, "Failed to read input from console"); + } + + // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested + if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown) + { + chIn = ir.Event.KeyEvent.uChar.UnicodeChar; + + if (0 == cchTotal) + { + cchTotal = cch; + cch *= 2; + StrAlloc(ppwzBuffer, cch); + } + + (*ppwzBuffer)[dwIndex] = chIn; + + if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex])) + { + *ppwzBuffer[dwIndex - 1] = L'\0'; + dwIndex -= 1; + break; + } + + ++dwIndex; + cchTotal--; + } + } + + *pcchSize = dwIndex; + } + else + { + // otherwise, the peek worked, and we have the end of a pipe + if (0 == dwRead) + ExitFunction1(hr = S_FALSE); + + cch = 8; + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + + for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + + cchTotalRead += cchRead; + if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead]) + { + psz[cchTotalRead - 1] = '\0'; // chop off the \r\n + cchTotalRead -= 1; + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + *pcchSize = cchTotalRead; + hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP); + } + +LExit: + ReleaseStr(psz); + + return hr; +} + + +/******************************************************************** + ConsoleReadStringA - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringA( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (ppszCharBuffer && *ppszCharBuffer == NULL) + { + do + { + hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleReadStringW - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringW( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, + const DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (*ppwzCharBuffer == NULL) + { + do + { + hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadHidden - set console input no echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadHidden(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + ::FlushConsoleInputBuffer(vhStdIn); + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) + { + ConExitOnLastError(hr, "failed to set console input mode to be hidden"); + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadNormal - reset to echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadNormal(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) + { + ConExitOnLastError(hr, "failed to set console input mode to be normal"); + } + +LExit: + return hr; +} + 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CrypExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) +#define CrypExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) +#define CrypExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) +#define CrypExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) +#define CrypExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CRYPUTIL, e, x, s, __VA_ARGS__) +#define CrypExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CRYPUTIL, g, x, s, __VA_ARGS__) + +static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL; +static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL; +static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL; +static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL; + +static HMODULE vhAdvApi32Dll = NULL; +static HMODULE vhCrypt32Dll = NULL; +static BOOL vfCrypInitialized = FALSE; + +// function definitions + +/******************************************************************** + CrypInitialize - initializes cryputil + +*********************************************************************/ +extern "C" HRESULT DAPI CrypInitialize( + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); + if (SUCCEEDED(hr)) + { + // Ignore failures - if these don't exist, we'll try the Crypt methods. + vpfnRtlEncryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction040")); + vpfnRtlDecryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction041")); + } + if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory) + { + hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll); + CrypExitOnFailure(hr, "Failed to load Crypt32.dll"); + + vpfnCryptProtectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory")); + if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory) + { + CrypExitWithLastError(hr, "Failed to load an encryption method"); + } + vpfnCryptUnprotectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory")); + if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory) + { + CrypExitWithLastError(hr, "Failed to load a decryption method"); + } + } + + vfCrypInitialized = TRUE; + +LExit: + return hr; +} + + +/******************************************************************** + CrypUninitialize - uninitializes cryputil + +*********************************************************************/ +extern "C" void DAPI CrypUninitialize( + ) +{ + if (vhAdvApi32Dll) + { + ::FreeLibrary(vhAdvApi32Dll); + vhAdvApi32Dll = NULL; + vpfnRtlEncryptMemory = NULL; + vpfnRtlDecryptMemory = NULL; + } + + if (vhCrypt32Dll) + { + ::FreeLibrary(vhCrypt32Dll); + vhCrypt32Dll = NULL; + vpfnCryptProtectMemory = NULL; + vpfnCryptUnprotectMemory = NULL; + } + + vfCrypInitialized = FALSE; +} + +extern "C" HRESULT DAPI CrypDecodeObject( + __in_z LPCSTR szStructType, + __in_ecount(cbData) const BYTE* pbData, + __in DWORD cbData, + __in DWORD dwFlags, + __out LPVOID* ppvObject, + __out_opt DWORD* pcbObject + ) +{ + HRESULT hr = S_OK; + LPVOID pvObject = NULL; + DWORD cbObject = 0; + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject)) + { + CrypExitWithLastError(hr, "Failed to decode object to determine size."); + } + + pvObject = MemAlloc(cbObject, TRUE); + CrypExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object."); + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject)) + { + CrypExitWithLastError(hr, "Failed to decode object."); + } + + *ppvObject = pvObject; + pvObject = NULL; + + if (pcbObject) + { + *pcbObject = cbObject; + } + +LExit: + ReleaseMem(pvObject); + + return hr; +} + + +extern "C" HRESULT DAPI CrypMsgGetParam( + __in HCRYPTMSG hCryptMsg, + __in DWORD dwType, + __in DWORD dwIndex, + __out LPVOID* ppvData, + __out_opt DWORD* pcbData + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb)) + { + CrypExitWithLastError(hr, "Failed to get crypt message parameter data size."); + } + + pv = MemAlloc(cb, TRUE); + CrypExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter."); + + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb)) + { + CrypExitWithLastError(hr, "Failed to get crypt message parameter."); + } + + *ppvData = pv; + pv = NULL; + + if (pcbData) + { + *pcbData = cb; + } + +LExit: + ReleaseMem(pv); + + return hr; +} + + +extern "C" HRESULT DAPI CrypHashFile( + __in_z LPCWSTR wzFilePath, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // open input file + hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + CrypExitWithLastError(hr, "Failed to open input file."); + } + + hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed); + CrypExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + + +extern "C" HRESULT DAPI CrypHashFileHandle( + __in HANDLE hFile, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ) +{ + HRESULT hr = S_OK; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + DWORD cbRead = 0; + BYTE rgbBuffer[4096] = { }; + const LARGE_INTEGER liZero = { }; + + // get handle to the crypto provider + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + CrypExitWithLastError(hr, "Failed to acquire crypto context."); + } + + // initiate hash + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) + { + CrypExitWithLastError(hr, "Failed to initiate hash."); + } + + for (;;) + { + // read data block + if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL)) + { + CrypExitWithLastError(hr, "Failed to read data block."); + } + + if (!cbRead) + { + break; // end of file + } + + // hash data block + if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0)) + { + CrypExitWithLastError(hr, "Failed to hash data block."); + } + } + + // get hash value + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + CrypExitWithLastError(hr, "Failed to get hash value."); + } + + if (pqwBytesHashed) + { + if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT)) + { + CrypExitWithLastError(hr, "Failed to get file pointer."); + } + } + +LExit: + if (hHash) + { + ::CryptDestroyHash(hHash); + } + if (hProv) + { + ::CryptReleaseContext(hProv, 0); + } + + return hr; +} + +HRESULT DAPI CrypHashBuffer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash + ) +{ + HRESULT hr = S_OK; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + DWORD cbDataHashed = 0; + SIZE_T cbTotal = 0; + SIZE_T cbRemaining = 0; + + // get handle to the crypto provider + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + CrypExitWithLastError(hr, "Failed to acquire crypto context."); + } + + // initiate hash + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) + { + CrypExitWithLastError(hr, "Failed to initiate hash."); + } + + do + { + cbRemaining = cbBuffer - cbTotal; + cbDataHashed = (DWORD)min(DWORD_MAX, cbRemaining); + if (!::CryptHashData(hHash, pbBuffer + cbTotal, cbDataHashed, 0)) + { + CrypExitWithLastError(hr, "Failed to hash data."); + } + + cbTotal += cbDataHashed; + } while (cbTotal < cbBuffer); + + // get hash value + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + CrypExitWithLastError(hr, "Failed to get hash value."); + } + +LExit: + if (hHash) + { + ::CryptDestroyHash(hHash); + } + if (hProv) + { + ::CryptReleaseContext(hProv, 0); + } + + return hr; +} + +HRESULT DAPI CrypEncryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ) +{ + HRESULT hr = E_FAIL; + + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) + { + hr = E_INVALIDARG; + } + else if (vpfnRtlEncryptMemory) + { + hr = static_cast(vpfnRtlEncryptMemory(pData, cbData, dwFlags)); + } + else if (vpfnCryptProtectMemory) + { + if (vpfnCryptProtectMemory(pData, cbData, dwFlags)) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } + CrypExitOnFailure(hr, "Failed to encrypt memory"); +LExit: + return hr; +} + +HRESULT DAPI CrypDecryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ) +{ + HRESULT hr = E_FAIL; + + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) + { + hr = E_INVALIDARG; + } + else if (vpfnRtlDecryptMemory) + { + hr = static_cast(vpfnRtlDecryptMemory(pData, cbData, dwFlags)); + } + else if (vpfnCryptUnprotectMemory) + { + if (vpfnCryptUnprotectMemory(pData, cbData, dwFlags)) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } + CrypExitOnFailure(hr, "Failed to decrypt memory"); +LExit: + return hr; +} + 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DepExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) +#define DepExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) +#define DepExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) +#define DepExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) +#define DepExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEPUTIL, e, x, s, __VA_ARGS__) +#define DepExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEPUTIL, g, x, s, __VA_ARGS__) + +#define ARRAY_GROWTH_SIZE 5 + +static LPCWSTR vcszVersionValue = L"Version"; +static LPCWSTR vcszDisplayNameValue = L"DisplayName"; +static LPCWSTR vcszMinVersionValue = L"MinVersion"; +static LPCWSTR vcszMaxVersionValue = L"MaxVersion"; +static LPCWSTR vcszAttributesValue = L"Attributes"; + +enum eRequiresAttributes { RequiresAttributesMinVersionInclusive = 256, RequiresAttributesMaxVersionInclusive = 512 }; + +// We write to Software\Classes explicitly based on the current security context instead of HKCR. +// See http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. +static LPCWSTR vsczRegistryRoot = L"Software\\Classes\\Installer\\Dependencies\\"; +static LPCWSTR vsczRegistryDependents = L"Dependents"; + +static HRESULT AllocDependencyKeyName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczKeyName + ); + +static HRESULT GetDependencyNameFromKey( + __in HKEY hkHive, + __in LPCWSTR wzKey, + __deref_out_z LPWSTR* psczName + ); + +DAPI_(HRESULT) DepGetProviderInformation( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __deref_out_z_opt LPWSTR* psczId, + __deref_out_z_opt LPWSTR* psczName, + __deref_out_z_opt LPWSTR* psczVersion + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the dependency key. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + + // Get the Id if requested and available. + if (psczId) + { + hr = RegReadString(hkKey, NULL, psczId); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + DepExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey); + } + + // Get the DisplayName if requested and available. + if (psczName) + { + hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + DepExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey); + } + + // Get the Version if requested and available. + if (psczVersion) + { + hr = RegReadString(hkKey, vcszVersionValue, psczVersion); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + DepExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepCheckDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes, + __in STRINGDICT_HANDLE sdDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + DWORD64 dw64Version = 0; + DWORD64 dw64MinVersion = 0; + DWORD64 dw64MaxVersion = 0; + BOOL fAllowEqual = FALSE; + LPWSTR sczName = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the key. If that fails, add the missing dependency key to the dependency array if it doesn't already exist. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); + + // If there are no registry values, consider the key orphaned and treat it as missing. + hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); + } + } + + // If the key was not found or the Version value was not found, add the missing dependency key to the dependency array. + if (E_FILENOTFOUND == hr) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey); + } + else + { + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, NULL); + DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + + // Check MinVersion if provided. + if (wzMinVersion) + { + if (*wzMinVersion) + { + hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion); + DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); + + fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; + if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey); + } + else + { + hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); + DepExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey); + + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); + DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + } + } + + // Check MaxVersion if provided. + if (wzMaxVersion) + { + if (*wzMaxVersion) + { + hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion); + DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); + + fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; + if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey); + } + else + { + hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); + DepExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey); + + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); + DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + } + } + +LExit: + ReleaseStr(sczName); + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepCheckDependents( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __reserved int /*iAttributes*/, + __in C_STRINGDICT_HANDLE sdIgnoredDependents, + __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, + __inout LPUINT pcDependents + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkProviderKey = NULL; + HKEY hkDependentsKey = NULL; + LPWSTR sczDependentKey = NULL; + LPWSTR sczDependentName = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the key. If that fails, the dependency information is corrupt. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkProviderKey); + DepExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey); + + // Try to open the dependencies key. If that does not exist, there are no dependents. + hr = RegOpen(hkProviderKey, vsczRegistryDependents, KEY_READ, &hkDependentsKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + + // Now enumerate the dependent keys. If they are not defined in the ignored list, add them to the array. + for (DWORD dwIndex = 0; ; ++dwIndex) + { + hr = RegKeyEnum(hkDependentsKey, dwIndex, &sczDependentKey); + if (E_NOMOREITEMS != hr) + { + DepExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey); + } + else + { + hr = S_OK; + break; + } + + // If the key isn't ignored, add it to the dependent array. + hr = DictKeyExists(sdIgnoredDependents, sczDependentKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + else + { + // Get the name of the dependent from the key. + hr = GetDependencyNameFromKey(hkHive, sczDependentKey, &sczDependentName); + DepExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey); + + hr = DepDependencyArrayAlloc(prgDependents, pcDependents, sczDependentKey, sczDependentName); + DepExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey); + } + } + +LExit: + ReleaseStr(sczDependentName); + ReleaseStr(sczDependentKey); + ReleaseRegKey(hkDependentsKey); + ReleaseRegKey(hkProviderKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepRegisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z LPCWSTR wzVersion, + __in_z LPCWSTR wzDisplayName, + __in_z_opt LPCWSTR wzId, + __in int iAttributes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + BOOL fCreated = FALSE; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Create the dependency key (or open it if it already exists). + hr = RegCreateEx(hkHive, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); + DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey); + + // Set the id if it was provided. + if (wzId) + { + hr = RegWriteString(hkKey, NULL, wzId); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId); + } + + // Set the version. + hr = RegWriteString(hkKey, vcszVersionValue, wzVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion); + + // Set the display name. + hr = RegWriteString(hkKey, vcszDisplayNameValue, wzDisplayName); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName); + + // Set the attributes if non-zero. + if (0 != iAttributes) + { + hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); + DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepDependentExists( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDependentKey = NULL; + HKEY hkDependentKey = NULL; + + // Format the provider dependents registry key. + hr = StrAllocFormatted(&sczDependentKey, L"%ls%ls\\%ls\\%ls", vsczRegistryRoot, wzDependencyProviderKey, vsczRegistryDependents, wzProviderKey); + DepExitOnFailure(hr, "Failed to format registry key to dependent."); + + hr = RegOpen(hkHive, sczDependentKey, KEY_READ, &hkDependentKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey); + } + +LExit: + ReleaseRegKey(hkDependentKey); + ReleaseStr(sczDependentKey); + + return hr; +} + +DAPI_(HRESULT) DepRegisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDependencyKey = NULL; + HKEY hkDependencyKey = NULL; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + BOOL fCreated = FALSE; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzDependencyProviderKey, &sczDependencyKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey); + + // Create the dependency key (or open it if it already exists). + hr = RegCreateEx(hkHive, sczDependencyKey, KEY_WRITE, FALSE, NULL, &hkDependencyKey, &fCreated); + DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey); + + // Create the subkey to register the dependent. + hr = StrAllocFormatted(&sczKey, L"%ls\\%ls", vsczRegistryDependents, wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + + hr = RegCreateEx(hkDependencyKey, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); + DepExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey); + + // Set the minimum version if not NULL. + hr = RegWriteString(hkKey, vcszMinVersionValue, wzMinVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion); + + // Set the maximum version if not NULL. + hr = RegWriteString(hkKey, vcszMaxVersionValue, wzMaxVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion); + + // Set the attributes if non-zero. + if (0 != iAttributes) + { + hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); + DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + ReleaseRegKey(hkDependencyKey); + ReleaseStr(sczDependencyKey); + + return hr; +} + +DAPI_(HRESULT) DepUnregisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Delete the entire key including all sub-keys. + hr = RegDelete(hkHive, sczKey, REG_KEY_DEFAULT, TRUE); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepUnregisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistryRoot = NULL; + HKEY hkDependencyProviderKey = NULL; + HKEY hkRegistryDependents = NULL; + DWORD cSubKeys = 0; + DWORD cValues = 0; + + // Open the root key. We may delete the wzDependencyProviderKey during clean up. + hr = RegOpen(hkHive, vsczRegistryRoot, KEY_READ, &hkRegistryRoot); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot); + } + else + { + ExitFunction(); + } + + // Try to open the dependency key. If that does not exist, simply return. + hr = RegOpen(hkRegistryRoot, wzDependencyProviderKey, KEY_READ, &hkDependencyProviderKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey); + } + else + { + ExitFunction(); + } + + // Try to open the dependents subkey to enumerate. + hr = RegOpen(hkDependencyProviderKey, vsczRegistryDependents, KEY_READ, &hkRegistryDependents); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + } + else + { + ExitFunction(); + } + + // Delete the wzProviderKey dependent sub-key. + hr = RegDelete(hkRegistryDependents, wzProviderKey, REG_KEY_DEFAULT, TRUE); + DepExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + + // If there are no remaining dependents, delete the Dependents subkey. + hr = RegQueryKey(hkRegistryDependents, &cSubKeys, NULL); + DepExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey); + + if (0 < cSubKeys) + { + ExitFunction(); + } + + // Release the handle to make sure it's deleted immediately. + ReleaseRegKey(hkRegistryDependents); + + // Fail if there are any subkeys since we just checked. + hr = RegDelete(hkDependencyProviderKey, vsczRegistryDependents, REG_KEY_DEFAULT, FALSE); + DepExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + + // If there are no values, delete the provider dependency key. + hr = RegQueryKey(hkDependencyProviderKey, NULL, &cValues); + DepExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey); + + if (0 == cValues) + { + // Release the handle to make sure it's deleted immediately. + ReleaseRegKey(hkDependencyProviderKey); + + // Fail if there are any subkeys since we just checked. + hr = RegDelete(hkRegistryRoot, wzDependencyProviderKey, REG_KEY_DEFAULT, FALSE); + DepExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey); + } + +LExit: + ReleaseRegKey(hkRegistryDependents); + ReleaseRegKey(hkDependencyProviderKey); + ReleaseRegKey(hkRegistryRoot); + + return hr; +} + +DAPI_(HRESULT) DepDependencyArrayAlloc( + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __in_z LPCWSTR wzKey, + __in_z_opt LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + UINT cRequired = 0; + DEPENDENCY* pDependency = NULL; + + hr = ::UIntAdd(*pcDependencies, 1, &cRequired); + DepExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array."); + + hr = MemEnsureArraySize(reinterpret_cast(prgDependencies), cRequired, sizeof(DEPENDENCY), ARRAY_GROWTH_SIZE); + DepExitOnFailure(hr, "Failed to allocate memory for the dependency array."); + + pDependency = static_cast(&(*prgDependencies)[*pcDependencies]); + DepExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid."); + + hr = StrAllocString(&(pDependency->sczKey), wzKey, 0); + DepExitOnFailure(hr, "Failed to allocate the string key in the dependency array."); + + if (wzName) + { + hr = StrAllocString(&(pDependency->sczName), wzName, 0); + DepExitOnFailure(hr, "Failed to allocate the string name in the dependency array."); + } + + // Update the number of current elements in the dependency array. + *pcDependencies = cRequired; + +LExit: + return hr; +} + +DAPI_(void) DepDependencyArrayFree( + __in_ecount(cDependencies) DEPENDENCY* rgDependencies, + __in UINT cDependencies + ) +{ + for (UINT i = 0; i < cDependencies; ++i) + { + ReleaseStr(rgDependencies[i].sczKey); + ReleaseStr(rgDependencies[i].sczName); + } + + ReleaseMem(rgDependencies); +} + +/*************************************************************************** + AllocDependencyKeyName - Allocates and formats the root registry key name. + +***************************************************************************/ +static HRESULT AllocDependencyKeyName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczKeyName + ) +{ + HRESULT hr = S_OK; + size_t cchName = 0; + size_t cchKeyName = 0; + + // Get the length of the static registry root once. + static size_t cchRegistryRoot = ::lstrlenW(vsczRegistryRoot); + + // Get the length of the dependency, and add to the length of the root. + hr = ::StringCchLengthW(wzName, STRSAFE_MAX_CCH, &cchName); + DepExitOnFailure(hr, "Failed to get string length of dependency name."); + + // Add the sizes together to allocate memory once (callee will add space for nul). + hr = ::SizeTAdd(cchRegistryRoot, cchName, &cchKeyName); + DepExitOnFailure(hr, "Failed to add the string lengths together."); + + // Allocate and concat the strings together. + hr = StrAllocString(psczKeyName, vsczRegistryRoot, cchKeyName); + DepExitOnFailure(hr, "Failed to allocate string for dependency registry root."); + + hr = StrAllocConcat(psczKeyName, wzName, cchName); + DepExitOnFailure(hr, "Failed to concatenate the dependency key name."); + +LExit: + return hr; +} + +/*************************************************************************** + GetDependencyNameFromKey - Attempts to name of the dependency from the key. + +***************************************************************************/ +static HRESULT GetDependencyNameFromKey( + __in HKEY hkHive, + __in LPCWSTR wzProviderKey, + __deref_out_z LPWSTR* psczName + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the dependency key. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + + // Get the DisplayName if available. + hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DictExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) +#define DictExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) +#define DictExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) +#define DictExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) +#define DictExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DICTUTIL, e, x, s, __VA_ARGS__) +#define DictExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DICTUTIL, g, x, s, __VA_ARGS__) + +// These should all be primes, and spaced reasonably apart (currently each is about 4x the last) +const DWORD MAX_BUCKET_SIZES[] = { + 503, + 2017, + 7937, + 32779, + 131111, + 524341, + 2097709, + 8390857, + 33563437, + 134253719, + 537014927, + 2148059509 + }; + +// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions +#define MAX_BUCKETS_TO_ITEMS_RATIO 8 + +enum DICT_TYPE +{ + DICT_INVALID = 0, + DICT_EMBEDDED_KEY = 1, + DICT_STRING_LIST = 2 +}; + +struct STRINGDICT_STRUCT +{ + DICT_TYPE dtType; + + // Optional flags to control the behavior of the dictionary. + DICT_FLAG dfFlags; + + // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated + DWORD dwBucketSizeIndex; + + // Number of items currently stored in the dict buckets + DWORD dwNumItems; + + // Byte offset of key within bucket value, for collision checking - see + // comments above DictCreateEmbeddedKey() implementation for further details + size_t cByteOffset; + + // The actual stored buckets + void **ppvBuckets; + + // The actual stored items in the order they were added (used for auto freeing or enumerating) + void **ppvItemList; + + // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm + void **ppvValueArray; +}; + +const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT); + +static HRESULT StringHash( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwNumBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwHash + ); +static BOOL IsMatchExact( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwMatchIndex, + __in_z LPCWSTR wzOriginalString + ); +static HRESULT GetValue( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out_opt void **ppvValue + ); +static HRESULT GetInsertIndex( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwBucketCount, + __in void **ppvBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ); +static HRESULT GetIndex( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ); +static LPCWSTR GetKey( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); +static HRESULT GrowDictionary( + __inout STRINGDICT_STRUCT *psd + ); +// These 2 helper functions allow us to safely handle dictutil consumers resizing +// the value array by storing "offsets" instead of raw void *'s in our buckets. +static void * TranslateOffsetToValue( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); +static void * TranslateValueToOffset( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); + +// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s). +// However, to support collision checking, the key needs to be represented in the "value" object (pointed to +// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer +// within the "value" object. Use the offsetof() macro to fill this out. +// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter, +// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide +// this parameter to dictutil if it is possible you will realloc the array. +// +// Use DictAddValue() and DictGetValue() with this dictionary type. +extern "C" HRESULT DAPI DictCreateWithEmbeddedKey( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in_opt void **ppvArray, + __in size_t cByteOffset, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + + // Allocate the handle + *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); + DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + + STRINGDICT_STRUCT *psd = static_cast(*psdHandle); + + // Fill out the new handle's values + psd->dtType = DICT_EMBEDDED_KEY; + psd->dfFlags = dfFlags; + psd->cByteOffset = cByteOffset; + psd->dwBucketSizeIndex = 0; + psd->dwNumItems = 0; + psd->ppvItemList = NULL; + psd->ppvValueArray = ppvArray; + + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime + // array based on expected number of items and items to buckets ratio + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end + // this loop past the end of the array! + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) + { + ++psd->dwBucketSizeIndex; + } + + // Finally, allocate our initial buckets + psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); + DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + +LExit: + return hr; +} + +// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type. +extern "C" HRESULT DAPI DictCreateStringList( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + + // Allocate the handle + *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); + DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + + STRINGDICT_STRUCT *psd = static_cast(*psdHandle); + + // Fill out the new handle's values + psd->dtType = DICT_STRING_LIST; + psd->dfFlags = dfFlags; + psd->cByteOffset = 0; + psd->dwBucketSizeIndex = 0; + psd->dwNumItems = 0; + psd->ppvItemList = NULL; + psd->ppvValueArray = NULL; + + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime + // array based on expected number of items and items to buckets ratio + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end + // this loop past the end of the array! + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) + { + ++psd->dwBucketSizeIndex; + } + + // Finally, allocate our initial buckets + psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); + DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictCreateStringListFromArray( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sd = NULL; + + hr = DictCreateStringList(&sd, cStringArray, dfFlags); + DictExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD i = 0; i < cStringArray; ++i) + { + const LPCWSTR wzKey = rgwzStringArray[i]; + + hr = DictKeyExists(sd, wzKey); + if (E_NOTFOUND != hr) + { + DictExitOnFailure(hr, "Failed to check the string dictionary."); + } + else + { + hr = DictAddKey(sd, wzKey); + DictExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey); + } + } + + *psdHandle = sd; + sd = NULL; + +LExit: + ReleaseDict(sd); + + return hr; +} + +extern "C" HRESULT DAPI DictCompareStringListToArray( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cStringArray; ++i) + { + hr = DictKeyExists(sdStringList, rgwzStringArray[i]); + if (E_NOTFOUND != hr) + { + DictExitOnFailure(hr, "Failed to check the string dictionary."); + ExitFunction1(hr = S_OK); + } + } + + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH)); + +LExit: + return hr; +} + +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) +extern "C" HRESULT DAPI DictAddKey( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString + ) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + if (DICT_STRING_LIST != psd->dtType) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) + { + hr = GrowDictionary(psd); + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr) + { + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + hr = S_OK; + } + } + DictExitOnFailure(hr, "Failed to grow dictionary"); + } + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex); + DictExitOnFailure(hr, "Failed to get index to insert into"); + + hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); + DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); + ++psd->dwNumItems; + + hr = StrAllocString(reinterpret_cast(&(psd->ppvBuckets[dwIndex])), pszString, 0); + DictExitOnFailure(hr, "Failed to allocate copy of string"); + + psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex]; + +LExit: + return hr; +} + +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) +extern "C" HRESULT DAPI DictAddValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in void *pvValue + ) +{ + HRESULT hr = S_OK; + void *pvOffset = NULL; + LPCWSTR wzKey = NULL; + DWORD dwIndex = 0; + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + DictExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + if (DICT_EMBEDDED_KEY != psd->dtType) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + wzKey = GetKey(psd, pvValue); + DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict"); + + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) + { + hr = GrowDictionary(psd); + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 ) + { + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + hr = S_OK; + } + } + DictExitOnFailure(hr, "Failed to grow dictionary"); + } + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex); + DictExitOnFailure(hr, "Failed to get index to insert into"); + + hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); + DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); + ++psd->dwNumItems; + + pvOffset = TranslateValueToOffset(psd, pvValue); + psd->ppvBuckets[dwIndex] = pvOffset; + psd->ppvItemList[psd->dwNumItems-1] = pvOffset; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictGetValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString, + __out void **ppvValue + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + const STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + if (DICT_EMBEDDED_KEY != psd->dtType) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + hr = GetValue(psd, pszString, ppvValue); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + DictExitOnFailure(hr, "Failed to call internal GetValue()"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictKeyExists( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + const STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + // This works with either type of dictionary + hr = GetValue(psd, pszString, NULL); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + DictExitOnFailure(hr, "Failed to call internal GetValue()"); + +LExit: + return hr; +} + +extern "C" void DAPI DictDestroy( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle + ) +{ + DWORD i; + + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + if (DICT_STRING_LIST == psd->dtType) + { + for (i = 0; i < psd->dwNumItems; ++i) + { + ReleaseStr(reinterpret_cast(psd->ppvItemList[i])); + } + } + + ReleaseMem(psd->ppvItemList); + ReleaseMem(psd->ppvBuckets); + ReleaseMem(psd); +} + +static HRESULT StringHash( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwNumBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwHash + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzKey = NULL; + LPWSTR sczNewKey = NULL; + DWORD result = 0; + + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) + { + hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0); + DictExitOnFailure(hr, "Failed to convert the string to upper-case."); + + wzKey = sczNewKey; + } + else + { + wzKey = pszString; + } + + while (*wzKey) + { + result = ~(*wzKey++ * 509) + result * 65599; + } + + *pdwHash = result % dwNumBuckets; + +LExit: + ReleaseStr(sczNewKey); + + return hr; +} + +static BOOL IsMatchExact( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwMatchIndex, + __in_z LPCWSTR wzOriginalString + ) +{ + LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex])); + DWORD dwFlags = 0; + + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) + { + dwFlags |= NORM_IGNORECASE; + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1)) + { + return TRUE; + } + + return FALSE; +} + +static HRESULT GetValue( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out_opt void **ppvValue + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + void *pvCandidateValue = NULL; + DWORD dwIndex = 0; + + DictExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); + DictExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]); + + // If no match exists in the dict + if (NULL == pvCandidateValue) + { + if (NULL != ppvValue) + { + *ppvValue = NULL; + } + ExitFunction1(hr = E_NOTFOUND); + } + + hr = GetIndex(psd, pszString, &dwIndex); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + DictExitOnFailure(hr, "Failed to find index to get"); + + if (NULL != ppvValue) + { + *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]); + } + +LExit: + if (FAILED(hr) && NULL != ppvValue) + { + *ppvValue = NULL; + } + + return hr; +} + +static HRESULT GetInsertIndex( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwBucketCount, + __in void **ppvBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + + hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate); + DictExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket +#pragma prefast(push) +#pragma prefast(disable:26007) + while (NULL != ppvBuckets[dwIndexCandidate]) +#pragma prefast(pop) + { + ++dwIndexCandidate; + + // If we got to the end of the array, wrap around to zero index + if (dwIndexCandidate >= dwBucketCount) + { + dwIndexCandidate = 0; + } + + // If we wrapped all the way back around to our original index, the dict is full - throw an error + if (dwIndexCandidate == dwOriginalIndexCandidate) + { + // The dict table is full - this error seems to be a reasonably close match + hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL); + DictExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString); + } + } + + *pdwOutput = dwIndexCandidate; + +LExit: + return hr; +} + +static HRESULT GetIndex( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); + DictExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + while (!IsMatchExact(psd, dwIndexCandidate, pszString)) + { + ++dwIndexCandidate; + + // If we got to the end of the array, wrap around to zero index + if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + dwIndexCandidate = 0; + } + + // If no match exists in the dict + if (NULL == psd->ppvBuckets[dwIndexCandidate]) + { + ExitFunction1(hr = E_NOTFOUND); + } + + // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such + if (dwIndexCandidate == dwOriginalIndexCandidate) + { + ExitFunction1(hr = E_NOTFOUND); + } + } + + *pdwOutput = dwIndexCandidate; + +LExit: + return hr; +} + +static LPCWSTR GetKey( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + const BYTE *lpByte = reinterpret_cast(pvValue); + + if (DICT_EMBEDDED_KEY == psd->dtType) + { + void *pvKey = reinterpret_cast(reinterpret_cast(pvValue) + psd->cByteOffset); + +#pragma prefast(push) +#pragma prefast(disable:26010) + return *(reinterpret_cast(pvKey)); +#pragma prefast(pop) + } + else + { + return (reinterpret_cast(lpByte)); + } +} + +static HRESULT GrowDictionary( + __inout STRINGDICT_STRUCT *psd + ) +{ + HRESULT hr = S_OK; + DWORD dwInsertIndex = 0; + LPCWSTR wzKey = NULL; + DWORD dwNewBucketSizeIndex = 0; + size_t cbAllocSize = 0; + void **ppvNewBuckets = NULL; + + dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1; + + if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL)); + } + + hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize); + DictExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary"); + + ppvNewBuckets = static_cast(MemAlloc(cbAllocSize, TRUE)); + DictExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]); + + for (DWORD i = 0; i < psd->dwNumItems; ++i) + { + wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i])); + DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value"); + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex); + DictExitOnFailure(hr, "Failed to get index to insert into"); + + ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i]; + } + + psd->dwBucketSizeIndex = dwNewBucketSizeIndex; + ReleaseMem(psd->ppvBuckets); + psd->ppvBuckets = ppvNewBuckets; + ppvNewBuckets = NULL; + +LExit: + ReleaseMem(ppvNewBuckets); + + return hr; +} + +static void * TranslateOffsetToValue( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + if (NULL == pvValue) + { + return NULL; + } + + // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value + if (NULL != psd->ppvValueArray) + { + return reinterpret_cast(reinterpret_cast(pvValue) + reinterpret_cast(*psd->ppvValueArray) - 1); + } + else + { + return pvValue; + } +} + +static void * TranslateValueToOffset( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + if (NULL != psd->ppvValueArray) + { + // 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 + return reinterpret_cast(reinterpret_cast(pvValue) - reinterpret_cast(*psd->ppvValueArray) + 1); + } + else + { + return pvValue; + } +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DirExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) +#define DirExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) +#define DirExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) +#define DirExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) +#define DirExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DIRUTIL, e, x, s, __VA_ARGS__) +#define DirExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DIRUTIL, g, x, s, __VA_ARGS__) + + +/******************************************************************* + DirExists + +*******************************************************************/ +extern "C" BOOL DAPI DirExists( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + Assert(wzPath); + + BOOL fExists = FALSE; + + DWORD dwAttributes = ::GetFileAttributesW(wzPath); + if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here + { + ExitFunction(); + } + + if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (pdwAttributes) + { + *pdwAttributes = dwAttributes; + } + + fExists = TRUE; + } + +LExit: + return fExists; +} + + +/******************************************************************* + DirCreateTempPath + + *******************************************************************/ +extern "C" HRESULT DAPI DirCreateTempPath( + __in_z LPCWSTR wzPrefix, + __out_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD cchPath + ) +{ + Assert(wzPrefix); + Assert(wzPath); + + HRESULT hr = S_OK; + + WCHAR wzDir[MAX_PATH]; + WCHAR wzFile[MAX_PATH]; + DWORD cch = 0; + + cch = ::GetTempPathW(countof(wzDir), wzDir); + if (!cch || cch >= countof(wzDir)) + { + DirExitWithLastError(hr, "Failed to GetTempPath."); + } + + if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) + { + DirExitWithLastError(hr, "Failed to GetTempFileName."); + } + + hr = ::StringCchCopyW(wzPath, cchPath, wzFile); + +LExit: + return hr; +} + + +/******************************************************************* + DirEnsureExists + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureExists( + __in_z LPCWSTR wzPath, + __in_opt LPSECURITY_ATTRIBUTES psa + ) +{ + HRESULT hr = S_OK; + UINT er; + + // try to create this directory + if (!::CreateDirectoryW(wzPath, psa)) + { + // if the directory already exists, bail + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + ExitFunction1(hr = S_OK); + } + 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. + { + ExitFunction1(hr = S_OK); + } + + // get the parent path and try to create it + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzPath); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + // if there is no parent directory fail + DirExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); + + *pwzLastSlash = L'\0'; // null terminate the parent path + hr = DirEnsureExists(wzPath, psa); // recurse! + *pwzLastSlash = L'\\'; // put the slash back + DirExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); + + // try to create the directory now that all parents are created + if (!::CreateDirectoryW(wzPath, psa)) + { + // if the directory already exists for some reason no error + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + } + else + { + hr = S_OK; + } + } + +LExit: + return hr; +} + + +/******************************************************************* + DirEnsureDelete - removes an entire directory structure + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureDelete( + __in_z LPCWSTR wzPath, + __in BOOL fDeleteFiles, + __in BOOL fRecurse + ) +{ + HRESULT hr = S_OK; + DWORD dwDeleteFlags = 0; + + dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0; + dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0; + + hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags); + return hr; +} + + +/******************************************************************* + DirEnsureDeleteEx - removes an entire directory structure + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureDeleteEx( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ) +{ + Assert(wzPath && *wzPath); + + HRESULT hr = S_OK; + DWORD er; + + DWORD dwAttrib; + HANDLE hFind = INVALID_HANDLE_VALUE; + LPWSTR sczDelete = NULL; + WIN32_FIND_DATAW wfd; + + BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); + BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); + BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); + WCHAR wzTempDirectory[MAX_PATH] = { }; + WCHAR wzTempPath[MAX_PATH] = { }; + + if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) + { + er = ::GetLastError(); + if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory. + { + er = ERROR_PATH_NOT_FOUND; + } + hr = HRESULT_FROM_WIN32(er); + DirExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); + } + + if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) + { + if (dwAttrib & FILE_ATTRIBUTE_READONLY) + { + if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL)) + { + DirExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); + } + } + + // If we're deleting files and/or child directories loop through the contents of the directory. + if (fDeleteFiles || fRecurse) + { + if (fScheduleDelete) + { + if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) + { + DirExitWithLastError(hr, "Failed to get temp directory."); + } + } + + // Delete everything in this directory. + hr = PathConcat(wzPath, L"*.*", &sczDelete); + DirExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); + + hFind = ::FindFirstFileW(sczDelete, &wfd); + if (INVALID_HANDLE_VALUE == hFind) + { + DirExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); + } + + do + { + // Skip the dot directories. + if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2]))) + { + continue; + } + + // For extra safety and to silence OACR. + wfd.cFileName[MAX_PATH - 1] = L'\0'; + + hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); + DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); + + if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + hr = PathBackslashTerminate(&sczDelete); + DirExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); + + hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call + if (FAILED(hr)) + { + // if we failed to delete a subdirectory, keep trying to finish any remaining files + ExitTraceSource(DUTIL_SOURCE_DIRUTIL, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); + hr = S_OK; + } + } + else if (fDeleteFiles) // this is a file, just delete it + { + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) + { + if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL)) + { + DirExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); + } + } + + if (!::DeleteFileW(sczDelete)) + { + if (fScheduleDelete) + { + if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) + { + DirExitWithLastError(hr, "Failed to get temp file to move to."); + } + + // Try to move the file to the temp directory then schedule for delete, + // otherwise just schedule for delete. + if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) + { + ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + } + else + { + ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + } + } + else + { + DirExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); + } + } + } + } while (::FindNextFileW(hFind, &wfd)); + + er = ::GetLastError(); + if (ERROR_NO_MORE_FILES == er) + { + hr = S_OK; + } + else + { + DirExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); + } + } + + if (!::RemoveDirectoryW(wzPath)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete) + { + if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) + { + hr = S_OK; + } + } + + DirExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); + } + } + else + { + hr = E_UNEXPECTED; + DirExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); + } + + Assert(S_OK == hr); + +LExit: + ReleaseFileFindHandle(hFind); + ReleaseStr(sczDelete); + + return hr; +} + + +/******************************************************************* +DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many + of its parents as possible. + + Returns: count of directories deleted. +*******************************************************************/ +extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot( + __in_z LPCWSTR wzPath, + __in DWORD /*dwFlags*/ + ) +{ + DWORD cDeletedDirs = 0; + LPWSTR sczPath = NULL; + + while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) + { + ++cDeletedDirs; + + HRESULT hr = PathGetParentPath(wzPath, &sczPath); + DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); + + wzPath = sczPath; + } + +LExit: + ReleaseStr(sczPath); + + return cDeletedDirs; +} + + +/******************************************************************* + DirGetCurrent - gets the current directory. + +*******************************************************************/ +extern "C" HRESULT DAPI DirGetCurrent( + __deref_out_z LPWSTR* psczCurrentDirectory + ) +{ + HRESULT hr = S_OK; + SIZE_T cch = 0; + + if (psczCurrentDirectory && *psczCurrentDirectory) + { + hr = StrMaxLength(*psczCurrentDirectory, &cch); + DirExitOnFailure(hr, "Failed to determine size of current directory."); + } + + DWORD cchRequired = ::GetCurrentDirectoryW((DWORD)min(DWORD_MAX, cch), 0 == cch ? NULL : *psczCurrentDirectory); + if (0 == cchRequired) + { + DirExitWithLastError(hr, "Failed to get current directory."); + } + else if (cch < cchRequired) + { + hr = StrAlloc(psczCurrentDirectory, cchRequired); + DirExitOnFailure(hr, "Failed to allocate string for current directory."); + + if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory)) + { + DirExitWithLastError(hr, "Failed to get current directory using allocated string."); + } + } + +LExit: + return hr; +} + + +/******************************************************************* + DirSetCurrent - sets the current directory. + +*******************************************************************/ +extern "C" HRESULT DAPI DirSetCurrent( + __in_z LPCWSTR wzDirectory + ) +{ + HRESULT hr = S_OK; + + if (!::SetCurrentDirectoryW(wzDirectory)) + { + DirExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); + } + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include +#include + + +// Exit macros +#define DlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) +#define DlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) +#define DlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) +#define DlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) +#define DlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DLUTIL, e, x, s, __VA_ARGS__) +#define DlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DLUTIL, g, x, s, __VA_ARGS__) + +static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024; +static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL }; + +// internal function declarations + +static HRESULT InitializeResume( + __in LPCWSTR wzDestinationPath, + __out LPWSTR* psczResumePath, + __out HANDLE* phResumeFile, + __out DWORD64* pdw64ResumeOffset + ); +static HRESULT GetResourceMetadata( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out DWORD64* pdw64ResourceSize, + __out FILETIME* pftResourceCreated + ); +static HRESULT DownloadResource( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_z LPCWSTR wzDestinationPath, + __in DWORD64 dw64AuthoredResourceLength, + __in DWORD64 dw64ResourceLength, + __in DWORD64 dw64ResumeOffset, + __in HANDLE hResumeFile, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ); +static HRESULT AllocateRangeRequestHeader( + __in DWORD64 dw64ResumeOffset, + __in DWORD64 dw64ResourceLength, + __deref_inout_z LPWSTR* psczHeader + ); +static HRESULT WriteToFile( + __in HINTERNET hUrl, + __in HANDLE hPayloadFile, + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD64 dw64ResourceLength, + __in LPBYTE pbData, + __in DWORD cbData, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback + ); +static HRESULT UpdateResumeOffset( + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD cbData + ); +static HRESULT MakeRequest( + __in HINTERNET hSession, + __inout_z LPWSTR* psczSourceUrl, + __in_z_opt LPCWSTR wzMethod, + __in_z_opt LPCWSTR wzHeaders, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out HINTERNET* phConnect, + __out HINTERNET* phUrl, + __out BOOL* pfRangeRequestsAccepted + ); +static HRESULT OpenRequest( + __in HINTERNET hConnect, + __in_z_opt LPCWSTR wzMethod, + __in INTERNET_SCHEME scheme, + __in_z LPCWSTR wzResource, + __in_z_opt LPCWSTR wzQueryString, + __in_z_opt LPCWSTR wzHeader, + __out HINTERNET* phUrl + ); +static HRESULT SendRequest( + __in HINTERNET hUrl, + __inout_z LPWSTR* psczUrl, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetry, + __out BOOL* pfRangesAccepted + ); +static HRESULT AuthenticationRequired( + __in HINTERNET hUrl, + __in long lHttpCode, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); +static HRESULT DownloadGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ); +static HRESULT DownloadSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ); +// function definitions + +extern "C" HRESULT DAPI DownloadUrl( + __in DOWNLOAD_SOURCE* pDownloadSource, + __in DWORD64 dw64AuthoredDownloadSize, + __in LPCWSTR wzDestinationPath, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ) +{ + HRESULT hr = S_OK; + LPWSTR sczUrl = NULL; + HINTERNET hSession = NULL; + DWORD dwTimeout = 0; + LPWSTR sczResumePath = NULL; + HANDLE hResumeFile = INVALID_HANDLE_VALUE; + DWORD64 dw64ResumeOffset = 0; + DWORD64 dw64Size = 0; + FILETIME ftCreated = { }; + + // Copy the download source into a working variable to handle redirects then + // open the internet session. + hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); + DlExitOnFailure(hr, "Failed to copy download source URL."); + + hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + DlExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); + + // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. + PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); + if (0 < dwTimeout) + { + dwTimeout *= 1000; // convert to milliseconds. + ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + } + + // Get the resource size and creation time from the internet. + hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated); + DlExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl); + + // Ignore failure to initialize resume because we will fall back to full download then + // download. + InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); + + hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate); + DlExitOnFailure(hr, "Failed to download URL: %ls", sczUrl); + + // Cleanup the resume file because we successfully downloaded the whole file. + if (sczResumePath && *sczResumePath) + { + ::DeleteFileW(sczResumePath); + } + +LExit: + ReleaseFileHandle(hResumeFile); + ReleaseStr(sczResumePath); + ReleaseInternet(hSession); + ReleaseStr(sczUrl); + + return hr; +} + + +// internal helper functions + +static HRESULT InitializeResume( + __in LPCWSTR wzDestinationPath, + __out LPWSTR* psczResumePath, + __out HANDLE* phResumeFile, + __out DWORD64* pdw64ResumeOffset + ) +{ + HRESULT hr = S_OK; + HANDLE hResumeFile = INVALID_HANDLE_VALUE; + DWORD cbTotalReadResumeData = 0; + DWORD cbReadData = 0; + + *pdw64ResumeOffset = 0; + + hr = DownloadGetResumePath(wzDestinationPath, psczResumePath); + DlExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); + + hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hResumeFile) + { + DlExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath); + } + + do + { + if (!::ReadFile(hResumeFile, reinterpret_cast(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) + { + DlExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath); + } + cbTotalReadResumeData += cbReadData; + } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); + + // Start over if we couldn't get a resume offset. + if (cbTotalReadResumeData != sizeof(DWORD64)) + { + *pdw64ResumeOffset = 0; + } + + *phResumeFile = hResumeFile; + hResumeFile = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hResumeFile); + return hr; +} + +static HRESULT GetResourceMetadata( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out DWORD64* pdw64ResourceSize, + __out FILETIME* pftResourceCreated + ) +{ + HRESULT hr = S_OK; + BOOL fRangeRequestsAccepted = TRUE; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + LONGLONG llLength = 0; + + hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); + DlExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl); + + hr = InternetGetSizeByHandle(hUrl, &llLength); + if (FAILED(hr)) + { + llLength = 0; + hr = S_OK; + } + + *pdw64ResourceSize = llLength; + + // Get the last modified time from the server, we'll use that as our downloaded time here. If + // the server time isn't available then use the local system time. + hr = InternetGetCreateTimeByHandle(hUrl, pftResourceCreated); + if (FAILED(hr)) + { + ::GetSystemTimeAsFileTime(pftResourceCreated); + hr = S_OK; + } + +LExit: + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + return hr; +} + +static HRESULT DownloadResource( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_z LPCWSTR wzDestinationPath, + __in DWORD64 dw64AuthoredResourceLength, + __in DWORD64 dw64ResourceLength, + __in DWORD64 dw64ResumeOffset, + __in HANDLE hResumeFile, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ) +{ + HRESULT hr = S_OK; + HANDLE hPayloadFile = INVALID_HANDLE_VALUE; + DWORD cbMaxData = 64 * 1024; // 64 KB + BYTE* pbData = NULL; + BOOL fRangeRequestsAccepted = TRUE; + LPWSTR sczRangeRequestHeader = NULL; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + LONGLONG llLength = 0; + + hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hPayloadFile) + { + DlExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath); + } + + // Allocate a memory block on a page boundary in case we want to do optimal writing. + pbData = static_cast(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); + DlExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); + + // Let's try downloading the file assuming that range requests are accepted. If range requests + // are not supported we'll have to start over and accept the fact that we only get one shot + // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't + // like files that big. + while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) + { + hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); + DlExitOnFailure(hr, "Failed to allocate range request header."); + + ReleaseNullInternet(hConnect); + ReleaseNullInternet(hUrl); + + hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); + DlExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl); + + // If we didn't get the size of the resource from the initial "HEAD" request + // then let's try to get the size from this "GET" request. + if (0 == dw64ResourceLength) + { + hr = InternetGetSizeByHandle(hUrl, &llLength); + if (SUCCEEDED(hr)) + { + dw64ResourceLength = llLength; + } + else // server didn't tell us the resource length. + { + // Fallback to the authored size of the resource. However, since we + // don't really know the size on the server, don't try to use + // range requests either. + dw64ResourceLength = dw64AuthoredResourceLength; + fRangeRequestsAccepted = FALSE; + } + } + + // If we just tried to do a range request and found out that it isn't supported, start over. + if (!fRangeRequestsAccepted) + { + // TODO: log a message that the server did not accept range requests. + dw64ResumeOffset = 0; + } + + hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache); + DlExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); + } + +LExit: + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + ReleaseStr(sczRangeRequestHeader); + if (pbData) + { + ::VirtualFree(pbData, 0, MEM_RELEASE); + } + ReleaseFileHandle(hPayloadFile); + + return hr; +} + +static HRESULT AllocateRangeRequestHeader( + __in DWORD64 dw64ResumeOffset, + __in DWORD64 dw64ResourceLength, + __deref_inout_z LPWSTR* psczHeader + ) +{ + HRESULT hr = S_OK; + + // If the remaining length is less that 2GB we'll be able to ask for everything. + DWORD64 dw64RemainingLength = dw64ResourceLength - dw64ResumeOffset; + if (DOWNLOAD_ENGINE_TWO_GIGABYTES > dw64RemainingLength) + { + // If we have a resume offset, let's download everything from there. Otherwise, we'll + // just get everything with no headers in the way. + if (0 < dw64ResumeOffset) + { + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset); + DlExitOnFailure(hr, "Failed to add range read header."); + } + else + { + ReleaseNullStr(*psczHeader); + } + } + else // we'll have to download in chunks. + { + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1); + DlExitOnFailure(hr, "Failed to add range read header."); + } + +LExit: + return hr; +} + +static HRESULT WriteToFile( + __in HINTERNET hUrl, + __in HANDLE hPayloadFile, + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD64 dw64ResourceLength, + __in LPBYTE pbData, + __in DWORD cbData, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback + ) +{ + HRESULT hr = S_OK; + DWORD cbReadData = 0; + + hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN); + DlExitOnFailure(hr, "Failed to seek to start point in file."); + + do + { + // Read bits from the internet. + if (!::InternetReadFile(hUrl, static_cast(pbData), cbData, &cbReadData)) + { + DlExitWithLastError(hr, "Failed while reading from internet."); + } + + // Write bits to disk (if there are any). + if (cbReadData) + { + DWORD cbTotalWritten = 0; + DWORD cbWritten = 0; + do + { + if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL)) + { + DlExitWithLastError(hr, "Failed to write data from internet."); + } + + cbTotalWritten += cbWritten; + } while (cbWritten && cbTotalWritten < cbReadData); + + // Ignore failure from updating resume file as this doesn't mean the download cannot succeed. + UpdateResumeOffset(pdw64ResumeOffset, hResumeFile, cbTotalWritten); + + if (pCallback && pCallback->pfnProgress) + { + hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile); + DlExitOnFailure(hr, "UX aborted on cache progress."); + } + } + } while (cbReadData); + +LExit: + return hr; +} + +static HRESULT UpdateResumeOffset( + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + + *pdw64ResumeOffset += cbData; + + if (INVALID_HANDLE_VALUE != hResumeFile) + { + DWORD cbTotalWrittenResumeData = 0; + DWORD cbWrittenResumeData = 0; + + hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN); + DlExitOnFailure(hr, "Failed to seek to start point in file."); + + do + { + // Ignore failure to write to the resume file as that should not prevent the download from happening. + if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL)) + { + DlExitOnFailure(hr, "Failed to seek to write to file."); + } + + cbTotalWrittenResumeData += cbWrittenResumeData; + } while (cbWrittenResumeData && sizeof(DWORD64) > cbTotalWrittenResumeData); + } + +LExit: + return hr; +} + +static HRESULT MakeRequest( + __in HINTERNET hSession, + __inout_z LPWSTR* psczSourceUrl, + __in_z_opt LPCWSTR wzMethod, + __in_z_opt LPCWSTR wzHeaders, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out HINTERNET* phConnect, + __out HINTERNET* phUrl, + __out BOOL* pfRangeRequestsAccepted + ) +{ + HRESULT hr = S_OK; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + URI_INFO uri = { }; + + // Try to open the URL. + BOOL fRetry; + do + { + fRetry = FALSE; + + // If the URL was opened close it, so we can reopen it again. + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + + // Open the url. + hr = UriCrackEx(*psczSourceUrl, &uri); + DlExitOnFailure(hr, "Failed to break URL into server and resource parts."); + + 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); + DlExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); + + // Best effort set the proxy username and password, if they were provided. + if ((wzUser && *wzUser) && (wzPassword && *wzPassword)) + { + if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser))) + { + ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword)); + } + } + + hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl); + DlExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl); + + hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted); + DlExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl); + } while (fRetry); + + // Okay, we're all ready to start downloading. Update the connection information. + *phConnect = hConnect; + hConnect = NULL; + *phUrl = hUrl; + hUrl = NULL; + +LExit: + UriInfoUninitialize(&uri); + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + + return hr; +} + +static HRESULT OpenRequest( + __in HINTERNET hConnect, + __in_z_opt LPCWSTR wzMethod, + __in INTERNET_SCHEME scheme, + __in_z LPCWSTR wzResource, + __in_z_opt LPCWSTR wzQueryString, + __in_z_opt LPCWSTR wzHeader, + __out HINTERNET* phUrl + ) +{ + HRESULT hr = S_OK; + DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD; + LPWSTR sczResource = NULL; + HINTERNET hUrl = NULL; + + if (INTERNET_SCHEME_HTTPS == scheme) + { + dwRequestFlags |= INTERNET_FLAG_SECURE; + } + else if (INTERNET_SCHEME_HTTP == scheme) + { + dwRequestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; + } + + // Allocate the resource name. + hr = StrAllocString(&sczResource, wzResource, 0); + DlExitOnFailure(hr, "Failed to allocate string for resource URI."); + + if (wzQueryString && *wzQueryString) + { + hr = StrAllocConcat(&sczResource, wzQueryString, 0); + DlExitOnFailure(hr, "Failed to append query strong to resource from URI."); + } + + // Open the request and add the header if provided. + hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL); + DlExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); + + if (wzHeader && *wzHeader) + { + if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast(-1), HTTP_ADDREQ_FLAG_COALESCE)) + { + DlExitWithLastError(hr, "Failed to add header to HTTP request."); + } + } + + *phUrl = hUrl; + hUrl = NULL; + +LExit: + ReleaseInternet(hUrl); + ReleaseStr(sczResource); + return hr; +} + +static HRESULT SendRequest( + __in HINTERNET hUrl, + __inout_z LPWSTR* psczUrl, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetry, + __out BOOL* pfRangesAccepted + ) +{ + HRESULT hr = S_OK; + BOOL fRetrySend = FALSE; + LONG lCode = 0; + + do + { + fRetrySend = FALSE; + + if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it. + LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl); + + // Try to get the HTTP status code and, if good, handle via the switch statement below but if it + // fails return the error code from the send request above as the result of the function. + HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); + DlExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); + } + else // get the http status code. + { + hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); + DlExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); + } + + switch (lCode) + { + case 200: // OK but range requests don't work. + *pfRangesAccepted = FALSE; + hr = S_OK; + break; + + case 206: // Partial content means that range requests work! + *pfRangesAccepted = TRUE; + hr = S_OK; + break; + + // redirection cases + case 301: __fallthrough; // file moved + case 302: __fallthrough; // temporary + case 303: // redirect method + hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl); + DlExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl); + + *pfRetry = TRUE; + break; + + // error cases + case 400: // bad request + hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); + break; + + case 401: __fallthrough; // unauthorized + case 407: __fallthrough; // proxy unauthorized + hr = AuthenticationRequired(hUrl, lCode, pAuthenticate, &fRetrySend, pfRetry); + break; + + case 403: // forbidden + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + break; + + case 404: // file not found + case 410: // gone + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + break; + + case 405: // method not allowed + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + break; + + case 408: __fallthrough; // request timedout + case 504: // gateway timeout + hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); + break; + + case 414: // request URI too long + hr = CO_E_PATHTOOLONG; + break; + + case 502: __fallthrough; // server (through a gateway) was not found + case 503: // server unavailable + hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + break; + + case 418: // I'm a teapot. + default: + // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway) + // do not overwrite the error code from the failed request. Otherwise, the error was unexpected. + if (SUCCEEDED(hr)) + { + hr = E_UNEXPECTED; + } + + LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl); + break; + } + } while (fRetrySend); + +LExit: + return hr; +} + +static HRESULT AuthenticationRequired( + __in HINTERNET hUrl, + __in long lHttpCode, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ) +{ + Assert(401 == lHttpCode || 407 == lHttpCode); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + *pfRetrySend = FALSE; + *pfRetry = FALSE; + + if (pAuthenticate && pAuthenticate->pfnAuthenticate) + { + hr = (*pAuthenticate->pfnAuthenticate)(pAuthenticate->pv, hUrl, lHttpCode, pfRetrySend, pfRetry); + } + + return hr; +} + + +static HRESULT DownloadGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); + DlExitOnFailure(hr, "Failed to create resume path."); + +LExit: + return hr; +} + +static HRESULT DownloadSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ) +{ + static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; + + HRESULT hr = S_OK; + DWORD dwResult = PROGRESS_CONTINUE; + LARGE_INTEGER liTotalSize = { }; + LARGE_INTEGER liTotalTransferred = { }; + + if (pCallback->pfnProgress) + { + liTotalSize.QuadPart = dw64Total; + liTotalTransferred.QuadPart = dw64Progress; + + dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); + switch (dwResult) + { + case PROGRESS_CONTINUE: + hr = S_OK; + break; + + case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? + case PROGRESS_STOP: + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + DlExitOnRootFailure(hr, "UX aborted on download progress."); + + case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. + pCallback->pfnProgress = NULL; + hr = S_OK; + break; + + default: + hr = E_UNEXPECTED; + DlExitOnRootFailure(hr, "Invalid return code from progress routine."); + } + } + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__) + +static PFN_ADJUSTWINDOWRECTEXFORDPI vpfnAdjustWindowRectExForDpi = NULL; +static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; +static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; +static PFN_SETPROCESSDPIAWARE vpfnSetProcessDPIAware = NULL; +static PFN_SETPROCESSDPIAWARENESS vpfnSetProcessDpiAwareness = NULL; +static PFN_SETPROCESSDPIAWARENESSCONTEXT vpfnSetProcessDpiAwarenessContext = NULL; + +static HMODULE vhShcoreDll = NULL; +static HMODULE vhUser32Dll = NULL; +static BOOL vfDpiuInitialized = FALSE; + +DAPI_(void) DpiuInitialize() +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnGetDpiForMonitor = reinterpret_cast(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor")); + vpfnSetProcessDpiAwareness = reinterpret_cast(::GetProcAddress(vhShcoreDll, "SetProcessDpiAwareness")); + } + + hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnAdjustWindowRectExForDpi = reinterpret_cast(::GetProcAddress(vhUser32Dll, "AdjustWindowRectExForDpi")); + vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); + vpfnSetProcessDPIAware = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDPIAware")); + vpfnSetProcessDpiAwarenessContext = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDpiAwarenessContext")); + } + + vfDpiuInitialized = TRUE; +} + +DAPI_(void) DpiuUninitialize() +{ + if (vhShcoreDll) + { + ::FreeLibrary(vhShcoreDll); + } + + if (vhUser32Dll) + { + ::FreeLibrary(vhUser32Dll); + } + + vhShcoreDll = NULL; + vhUser32Dll = NULL; + vpfnAdjustWindowRectExForDpi = NULL; + vpfnGetDpiForMonitor = NULL; + vpfnGetDpiForWindow = NULL; + vfDpiuInitialized = FALSE; +} + +DAPI_(void) DpiuAdjustWindowRect( + __in RECT* pWindowRect, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle, + __in UINT nDpi + ) +{ + if (WS_SYSMENU & dwStyle) + { + dwStyle |= WS_CAPTION; // WS_CAPTION is required with WS_SYSMENU, AdjustWindowRect* won't work properly when it's not specified. + } + + if (vpfnAdjustWindowRectExForDpi) + { + vpfnAdjustWindowRectExForDpi(pWindowRect, dwStyle, fMenu, dwExStyle, nDpi); + } + else + { + ::AdjustWindowRectEx(pWindowRect, dwStyle, fMenu, dwExStyle); + } +} + +DAPI_(HRESULT) DpiuGetMonitorContextFromPoint( + __in const POINT* pt, + __out DPIU_MONITOR_CONTEXT** ppMonitorContext + ) +{ + HRESULT hr = S_OK; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + HMONITOR hMonitor = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + HDC hdc = NULL; + + pMonitorContext = reinterpret_cast(MemAlloc(sizeof(DPIU_MONITOR_CONTEXT), TRUE)); + DpiuExitOnNull(pMonitorContext, hr, E_OUTOFMEMORY, "Failed to allocate memory for DpiuMonitorContext."); + + hMonitor = ::MonitorFromPoint(*pt, MONITOR_DEFAULTTONEAREST); + DpiuExitOnNull(hMonitor, hr, E_FAIL, "Failed to get monitor from point."); + + pMonitorContext->mi.cbSize = sizeof(pMonitorContext->mi); + if (!::GetMonitorInfoW(hMonitor, &pMonitorContext->mi)) + { + DpiuExitOnFailure(hr = E_OUTOFMEMORY, "Failed to get monitor info for point."); + } + + if (vpfnGetDpiForMonitor) + { + hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + DpiuExitOnFailure(hr, "Failed to get DPI for monitor."); + + pMonitorContext->nDpi = dpiX; + } + else + { + hdc = ::CreateDCW(L"DISPLAY", pMonitorContext->mi.szDevice, NULL, NULL); + DpiuExitOnNull(hdc, hr, E_OUTOFMEMORY, "Failed to get device context for monitor."); + + pMonitorContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); + } + + *ppMonitorContext = pMonitorContext; + pMonitorContext = NULL; + +LExit: + if (hdc) + { + ::ReleaseDC(NULL, hdc); + } + + MemFree(pMonitorContext); + + return hr; +} + +DAPI_(void) DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ) +{ + HRESULT hr = S_OK; + HMONITOR hMonitor = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + HDC hdc = NULL; + + pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI; + + if (vpfnGetDpiForWindow) + { + pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd); + ExitFunction(); + } + + if (vpfnGetDpiForMonitor) + { + hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + if (SUCCEEDED(hr)) + { + pWindowContext->nDpi = dpiX; + ExitFunction(); + } + } + } + + hdc = ::GetDC(hWnd); + if (hdc) + { + pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); + } + +LExit: + if (hdc) + { + ::ReleaseDC(hWnd, hdc); + } +} + +DAPI_(int) DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ) +{ + return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI); +} + +DAPI_(HRESULT) DpiuSetProcessDpiAwareness( + __in DPIU_AWARENESS supportedAwareness, + __in_opt DPIU_AWARENESS* pSelectedAwareness + ) +{ + HRESULT hr = S_OK; + DPIU_AWARENESS selectedAwareness = DPIU_AWARENESS_NONE; + DPI_AWARENESS_CONTEXT awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE; + PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE; + + if (vpfnSetProcessDpiAwarenessContext) + { + if (DPIU_AWARENESS_PERMONITORV2 & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + selectedAwareness = DPIU_AWARENESS_PERMONITORV2; + } + else if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; + selectedAwareness = DPIU_AWARENESS_PERMONITOR; + } + else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + selectedAwareness = DPIU_AWARENESS_SYSTEM; + } + else if (DPIU_AWARENESS_GDISCALED & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED; + selectedAwareness = DPIU_AWARENESS_GDISCALED; + } + + if (!vpfnSetProcessDpiAwarenessContext(awarenessContext)) + { + DpiuExitOnLastError(hr, "Failed to set process DPI awareness context."); + } + } + else if (vpfnSetProcessDpiAwareness) + { + if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) + { + awareness = PROCESS_PER_MONITOR_DPI_AWARE; + selectedAwareness = DPIU_AWARENESS_PERMONITOR; + } + else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) + { + awareness = PROCESS_SYSTEM_DPI_AWARE; + selectedAwareness = DPIU_AWARENESS_SYSTEM; + } + + hr = vpfnSetProcessDpiAwareness(awareness); + DpiuExitOnFailure(hr, "Failed to set process DPI awareness."); + } + else if (vpfnSetProcessDPIAware && (DPIU_AWARENESS_SYSTEM & supportedAwareness)) + { + selectedAwareness = DPIU_AWARENESS_SYSTEM; + if (!vpfnSetProcessDPIAware()) + { + DpiuExitOnLastError(hr, "Failed to set process DPI aware."); + } + } + +LExit: + if (pSelectedAwareness) + { + *pSelectedAwareness = selectedAwareness; + } + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) +#define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) +#define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) +#define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) +#define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__) +#define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__) + +// No need for OACR to warn us about using non-unicode APIs in this file. +#pragma prefast(disable:25068) + +// Asserts & Tracing + +const int DUTIL_STRING_BUFFER = 1024; +static HMODULE Dutil_hAssertModule = NULL; +static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL; +static BOOL Dutil_fNoAsserts = FALSE; +static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD; +static BOOL Dutil_fTraceFilenames = FALSE; +static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL; + + +DAPI_(HRESULT) DutilInitialize( + __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback + ) +{ + HRESULT hr = S_OK; + + vpfnTraceErrorCallback = pfnTraceErrorCallback; + + return hr; +} + + +DAPI_(void) DutilUninitialize() +{ + vpfnTraceErrorCallback = NULL; +} + +/******************************************************************* +Dutil_SetAssertModule + +*******************************************************************/ +extern "C" void DAPI Dutil_SetAssertModule( + __in HMODULE hAssertModule + ) +{ + Dutil_hAssertModule = hAssertModule; +} + + +/******************************************************************* +Dutil_SetAssertDisplayFunction + +*******************************************************************/ +extern "C" void DAPI Dutil_SetAssertDisplayFunction( + __in DUTIL_ASSERTDISPLAYFUNCTION pfn + ) +{ + Dutil_pfnDisplayAssert = pfn; +} + + +/******************************************************************* +Dutil_AssertMsg + +*******************************************************************/ +extern "C" void DAPI Dutil_AssertMsg( + __in_z LPCSTR szMessage + ) +{ + static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts) + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int id = IDRETRY; + HKEY hkDebug = NULL; + HANDLE hAssertFile = INVALID_HANDLE_VALUE; + char szPath[MAX_PATH] = { }; + DWORD cch = 0; + + if (fInAssert) + { + return; + } + fInAssert = TRUE; + + char szMsg[DUTIL_STRING_BUFFER]; + hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); + DExitOnFailure(hr, "failed to copy message while building assert message"); + + if (Dutil_pfnDisplayAssert) + { + // call custom function to display the assert string + if (!Dutil_pfnDisplayAssert(szMsg)) + { + ExitFunction(); + } + } + else + { + OutputDebugStringA(szMsg); + } + + if (!Dutil_fNoAsserts) + { + er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug); + if (ERROR_SUCCESS == er) + { + cch = countof(szPath); + er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast(szPath), &cch); + szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that. + if (ERROR_SUCCESS == er) + { + hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hAssertFile) + { + if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END)) + { + if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n"))) + { + ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); + } + } + } + } + } + + // if anything went wrong while fooling around with the registry, just show the usual assert dialog box + if (ERROR_SUCCESS != er) + { + hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); + DExitOnFailure(hr, "failed to concat string while building assert message"); + + id = ::MessageBoxA(0, szMsg, "Debug Assert Message", + MB_SERVICE_NOTIFICATION | MB_TOPMOST | + MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE); + } + } + + if (id == IDABORT) + { + if (Dutil_hAssertModule) + { + ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath)); + + 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()); + if (SUCCEEDED(hr)) + { + ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK); + } + } + + ::DebugBreak(); + } + else if (id == IDIGNORE) + { + Dutil_fNoAsserts = TRUE; + } + +LExit: + ReleaseFileHandle(hAssertFile); + ReleaseRegKey(hkDebug); + fInAssert = FALSE; +} + + +/******************************************************************* +Dutil_Assert + +*******************************************************************/ +extern "C" void DAPI Dutil_Assert( + __in_z LPCSTR szFile, + __in int iLine + ) +{ + HRESULT hr = S_OK; + char szMessage[DUTIL_STRING_BUFFER] = { }; + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine); + if (SUCCEEDED(hr)) + { + Dutil_AssertMsg(szMessage); + } + else + { + Dutil_AssertMsg("Assert failed to build string"); + } +} + + +/******************************************************************* +Dutil_AssertSz + +*******************************************************************/ +extern "C" void DAPI Dutil_AssertSz( + __in_z LPCSTR szFile, + __in int iLine, + __in_z __format_string LPCSTR szMsg + ) +{ + HRESULT hr = S_OK; + char szMessage[DUTIL_STRING_BUFFER] = { }; + + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg); + if (SUCCEEDED(hr)) + { + Dutil_AssertMsg(szMessage); + } + else + { + Dutil_AssertMsg("Assert failed to build string"); + } +} + + +/******************************************************************* +Dutil_TraceSetLevel + +*******************************************************************/ +extern "C" void DAPI Dutil_TraceSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fTraceFilenames + ) +{ + Dutil_rlCurrentTrace = rl; + Dutil_fTraceFilenames = fTraceFilenames; +} + + +/******************************************************************* +Dutil_TraceGetLevel + +*******************************************************************/ +extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel() +{ + return Dutil_rlCurrentTrace; +} + + +/******************************************************************* +Dutil_Trace + +*******************************************************************/ +extern "C" void DAPIV Dutil_Trace( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level"); + + HRESULT hr = S_OK; + char szOutput[DUTIL_STRING_BUFFER] = { }; + char szMsg[DUTIL_STRING_BUFFER] = { }; + + if (Dutil_rlCurrentTrace < rl) + { + return; + } + + va_list args; + va_start(args, szFormat); + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); + va_end(args); + + if (SUCCEEDED(hr)) + { + LPCSTR szPrefix = "Trace/u"; + switch (rl) + { + case REPORT_STANDARD: + szPrefix = "Trace/s"; + break; + case REPORT_VERBOSE: + szPrefix = "Trace/v"; + break; + case REPORT_DEBUG: + szPrefix = "Trace/d"; + break; + } + + if (Dutil_fTraceFilenames) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput); + } + + if (SUCCEEDED(hr)) + { + OutputDebugStringA(szMsg); + } + // else fall through to the case below + } + + if (FAILED(hr)) + { + if (Dutil_fTraceFilenames) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n"); + } + + szMsg[countof(szMsg)-1] = '\0'; + OutputDebugStringA(szMsg); + } +} + + +/******************************************************************* +Dutil_TraceError + +*******************************************************************/ +extern "C" void DAPIV Dutil_TraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + char szOutput[DUTIL_STRING_BUFFER] = { }; + char szMsg[DUTIL_STRING_BUFFER] = { }; + + // if this is NOT an error report and we're not logging at this level, bail + if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) + { + return; + } + + va_list args; + va_start(args, szFormat); + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); + va_end(args); + + if (SUCCEEDED(hr)) + { + if (Dutil_fTraceFilenames) + { + if (FAILED(hrError)) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput); + } + } + else + { + if (FAILED(hrError)) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput); + } + } + + if (SUCCEEDED(hr)) + { + OutputDebugStringA(szMsg); + } + // else fall through to the failure case below + } + + if (FAILED(hr)) + { + if (Dutil_fTraceFilenames) + { + if (FAILED(hrError)) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine); + } + } + else + { + if (FAILED(hrError)) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n"); + } + } + + szMsg[countof(szMsg)-1] = '\0'; + OutputDebugStringA(szMsg); + } +} + + +DAPIV_(void) Dutil_TraceErrorSource( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hr, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + // if this is NOT an error report and we're not logging at this level, bail + if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) + { + return; + } + + if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback) + { + va_list args; + va_start(args, szFormat); + vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args); + va_end(args); + } +} + + +/******************************************************************* +Dutil_RootFailure + +*******************************************************************/ +extern "C" void DAPI Dutil_RootFailure( + __in_z LPCSTR szFile, + __in int iLine, + __in HRESULT hrError + ) +{ +#ifndef DEBUG + UNREFERENCED_PARAMETER(szFile); + UNREFERENCED_PARAMETER(iLine); + UNREFERENCED_PARAMETER(hrError); +#endif // DEBUG + + TraceError(hrError, "Root failure at %s:%d", szFile, iLine); +} + +/******************************************************************* + LoadSystemLibrary - Fully qualifies the path to a module in the + Windows system directory and loads it. + + Returns + E_MODNOTFOUND - The module could not be found. + * - Another error occured. +********************************************************************/ +extern "C" HRESULT DAPI LoadSystemLibrary( + __in_z LPCWSTR wzModuleName, + __out HMODULE *phModule + ) +{ + HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL); + return hr; +} + +/******************************************************************* + LoadSystemLibraryWithPath - Fully qualifies the path to a module in + the Windows system directory and loads it + and returns the path + + Returns + E_MODNOTFOUND - The module could not be found. + * - Another error occured. +********************************************************************/ +extern "C" HRESULT DAPI LoadSystemLibraryWithPath( + __in_z LPCWSTR wzModuleName, + __out HMODULE *phModule, + __deref_out_z_opt LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + DWORD cch = 0; + WCHAR wzPath[MAX_PATH] = { }; + + cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); + DExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); + + if (L'\\' != wzPath[cch - 1]) + { + hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); + DExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); + } + + hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); + DExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); + + *phModule = ::LoadLibraryW(wzPath); + DExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); + + if (psczPath) + { + hr = StrAllocString(psczPath, wzPath, MAX_PATH); + DExitOnFailure(hr, "Failed to copy the path to library."); + } + +LExit: + return hr; +} 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 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + MS-RL + https://github.com/wixtoolset/dutil + false + $description$ + $copyright$ + + + + + + + + + + + + + + + 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 @@ + + + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {1244E671-F108-4334-BA52-8A7517F26ECD} + StaticLibrary + dutil + true + v142 + MultiByte + WiX Toolset native library foundation + WixToolset.DUtil + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + 4091;4458 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + + \ 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define EseExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) +#define EseExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) +#define EseExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) +#define EseExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) +#define EseExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ESEUTIL, e, x, s, __VA_ARGS__) +#define EseExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ESEUTIL, g, x, s, __VA_ARGS__) + +struct ESE_QUERY +{ + ESE_QUERY_TYPE qtQueryType; + BOOL fIndexRangeSet; + + JET_SESID jsSession; + JET_TABLEID jtTable; + + DWORD dwColumns; + void *pvData[10]; // The data queried for for this column + DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData +}; + +// Todo: convert more JET_ERR to HRESULTS here +HRESULT HresultFromJetError(JET_ERR jEr) +{ + HRESULT hr = S_OK; + + switch (jEr) + { + case JET_errSuccess: + return S_OK; + + case JET_wrnNyi: + return E_NOTIMPL; + break; + + case JET_errOutOfMemory: + hr = E_OUTOFMEMORY; + break; + + case JET_errInvalidParameter: __fallthrough; + case JET_errInvalidInstance: + hr = E_INVALIDARG; + break; + + case JET_errDatabaseInUse: + hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE); + break; + + case JET_errKeyDuplicate: + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + break; + + case JET_errInvalidSystemPath: __fallthrough; + case JET_errInvalidLogDirectory: __fallthrough; + case JET_errInvalidPath: __fallthrough; + case JET_errDatabaseInvalidPath: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); + break; + + case JET_errDatabaseLocked: + hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT); + break; + + case JET_errInvalidDatabase: + hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION); + break; + + case JET_errCallbackNotResolved: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + break; + + case JET_errNoCurrentRecord: __fallthrough; + case JET_errRecordNotFound: __fallthrough; + case JET_errFileNotFound: __fallthrough; + case JET_errObjectNotFound: + hr = E_NOTFOUND; + break; + + case JET_wrnBufferTruncated: + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + break; + + case JET_errFileAccessDenied: + hr = E_ACCESSDENIED; + break; + + default: + hr = E_FAIL; + } + + // 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 + ExitTraceSource(DUTIL_SOURCE_ESEUTIL, hr, "Encountered Jet Error: 0x%08x", jEr); + + return hr; +} + +#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }} +#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; }} + +HRESULT DAPI EseBeginSession( + __out JET_INSTANCE *pjiInstance, + __out JET_SESID *pjsSession, + __in_z LPCWSTR pszInstance, + __in_z LPCWSTR pszPath + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiInstance = NULL; + LPSTR pszAnsiPath = NULL; + + hr = DirEnsureExists(pszPath, NULL); + EseExitOnFailure(hr, "Failed to ensure database directory exists"); + + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, + // likely breaking everyone with unicode characters in their path. + hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting instance name to ansi"); + + hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting session path name to ansi"); + + jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance); + ExitOnJetFailure(jEr, hr, "Failed to create instance"); + + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath); + ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath); + + // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution) + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath); + ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath); + + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter"); + + // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too) + + jEr = JetInit(pjiInstance); + ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance"); + + jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL); + ExitOnJetFailure(jEr, hr, "Failed to begin jet session"); + +LExit: + ReleaseStr(pszAnsiInstance); + ReleaseStr(pszAnsiPath); + + return hr; +} + +HRESULT DAPI EseEndSession( + __in JET_INSTANCE jiInstance, + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetEndSession(jsSession, 0); + ExitOnJetFailure(jEr, hr, "Failed to end jet session"); + + jEr = JetTerm(jiInstance); + ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance"); + +LExit: + return hr; +} + +// Utility function used by EnsureSchema() +HRESULT AllocColumnCreateStruct( + __in const ESE_TABLE_SCHEMA *ptsSchema, + __deref_out JET_COLUMNCREATE **ppjccColumnCreate + ) +{ + HRESULT hr = S_OK; + DWORD_PTR i; + size_t cbAllocSize = 0; + + hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize)); + EseExitOnFailure(hr, "Maximum allocation exceeded."); + + *ppjccColumnCreate = static_cast(MemAlloc(cbAllocSize, TRUE)); + EseExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database"); + + for (i = 0; i < ptsSchema->dwColumns; ++i) + { + (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE); + + hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName); + + (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType; + + if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 256; + } + else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 2147483648; + (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged + ptsSchema->pcsColumns[i].fNullable = TRUE; + } + else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 4; + + if (ptsSchema->pcsColumns[i].fAutoIncrement) + { + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement; + } + } + + if (!(ptsSchema->pcsColumns[i].fNullable)) + { + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL; + } + + (*ppjccColumnCreate)[i].pvDefault = NULL; + (*ppjccColumnCreate)[i].cbDefault = 0; + (*ppjccColumnCreate)[i].cp = 1200; + (*ppjccColumnCreate)[i].columnid = 0; + (*ppjccColumnCreate)[i].err = 0; + } + +LExit: + return hr; +} + +HRESULT FreeColumnCreateStruct( + __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate, + __in DWORD dwColumns + ) +{ + HRESULT hr = S_OK; + DWORD i; + + for (i = 0; i < dwColumns; ++i) + { + ReleaseStr((pjccColumnCreate[i]).szColumnName); + } + + hr = MemFree(pjccColumnCreate); + EseExitOnFailure(hr, "Failed to release core column create struct"); + +LExit: + return hr; +} + +// Utility function used by EnsureSchema() +HRESULT AllocIndexCreateStruct( + __in const ESE_TABLE_SCHEMA *ptsSchema, + __deref_out JET_INDEXCREATE **ppjicIndexCreate + ) +{ + HRESULT hr = S_OK; + LPSTR pszMultiSzKeys = NULL; + LPSTR pszIndexName = NULL; + LPSTR pszTempString = NULL; + BOOL fKeyColumns = FALSE; + DWORD_PTR i; + + for (i=0; i < ptsSchema->dwColumns; ++i) + { + if (ptsSchema->pcsColumns[i].fKey) + { + hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName); + + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0); + EseExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString); + + hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0); + EseExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString); + + ReleaseNullStr(pszTempString); + + // All question marks will be converted to null characters later; this is just to trick dutil + // into letting us create an ansi, double-null-terminated list of single-null-terminated strings + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0); + EseExitOnFailure(hr, "Failed to append placeholder character to multisz string: %hs", pszMultiSzKeys); + + // Record that at least one key column was found + fKeyColumns = TRUE; + } + } + + // If no key columns were found, don't create an index - just return + if (!fKeyColumns) + { + ExitFunction1(hr = S_OK); + } + + hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName); + + hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0); + EseExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName); + + *ppjicIndexCreate = static_cast(MemAlloc(sizeof(JET_INDEXCREATE), TRUE)); + EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); + + // Record the size including both null terminators - the struct requires this + size_t cchSize = 0; + hr = ::StringCchLengthA(pszMultiSzKeys, STRSAFE_MAX_LENGTH, &cchSize); + EseExitOnRootFailure(hr, "Failed to get size of keys string"); + + ++cchSize; // add 1 to include null character at the end + + // At this point convert all question marks to null characters + for (i = 0; i < cchSize; ++i) + { + if ('?' == pszMultiSzKeys[i]) + { + pszMultiSzKeys[i] = '\0'; + } + } + + (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE); + (*ppjicIndexCreate)->szIndexName = pszIndexName; + (*ppjicIndexCreate)->szKey = pszMultiSzKeys; + (*ppjicIndexCreate)->cbKey = (DWORD)cchSize; + (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary; + (*ppjicIndexCreate)->ulDensity = 80; + (*ppjicIndexCreate)->lcid = 1033; + (*ppjicIndexCreate)->pidxunicode = NULL; + (*ppjicIndexCreate)->cbVarSegMac = 0; + (*ppjicIndexCreate)->rgconditionalcolumn = NULL; + (*ppjicIndexCreate)->cConditionalColumn = 0; + (*ppjicIndexCreate)->err = 0; + +LExit: + ReleaseStr(pszTempString); + + return hr; +} + +HRESULT EnsureSchema( + __in JET_DBID jdbDb, + __in JET_SESID jsSession, + __in ESE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BOOL fTransaction = FALSE; + DWORD dwTable; + DWORD dwColumn; + JET_TABLECREATE jtTableCreate = { }; + + // Set parameters which apply to all tables here + jtTableCreate.cbStruct = sizeof(jtTableCreate); + jtTableCreate.ulPages = 100; + jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value" + jtTableCreate.cIndexes = 1; + + hr = EseBeginTransaction(jsSession); + EseExitOnFailure(hr, "Failed to begin transaction to create tables"); + fTransaction = TRUE; + + for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable) + { + // Don't free this pointer - it's just a shortcut to the current table's name within the struct + LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName; + + // Ensure table exists + hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable); + if (E_NOTFOUND == hr) // if the table is missing, create it + { + // Fill out the JET_TABLECREATE struct + hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting table name to ansi"); + + hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate); + EseExitOnFailure(hr, "Failed to allocate column create struct"); + + hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate); + EseExitOnFailure(hr, "Failed to allocate index create struct"); + + jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns; + jtTableCreate.tableid = NULL; + + // TODO: Investigate why we can't create a table without a key column? + // Actually create the table using our JET_TABLECREATE struct + jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate); + ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName); + + // Record the table ID in our cache + pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid; + + // Record the column IDs in our cache + for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) + { + pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid; + } + + // Free and NULL things we allocated in this struct + ReleaseNullStr(jtTableCreate.szTableName); + + hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); + EseExitOnFailure(hr, "Failed to free column create struct"); + jtTableCreate.rgcolumncreate = NULL; + } + else + { + // If the table already exists, grab the column ids and put them into our cache + for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) + { + // Don't free this - it's just a shortcut to the current column within the struct + ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]); + ULONG ulColumnSize = 0; + BOOL fNullable = pcsColumn->fNullable; + + // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out! + if (JET_coltypText == pcsColumn->jcColumnType) + { + ulColumnSize = 256; + } + else if (JET_coltypLongText == pcsColumn->jcColumnType) + { + ulColumnSize = 2147483648; + fNullable = TRUE; + } + else if (JET_coltypLong == pcsColumn->jcColumnType) + { + ulColumnSize = 4; + fNullable = TRUE; + } + + hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn); + EseExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName); + } + } + } + +LExit: + ReleaseStr(jtTableCreate.szTableName); + + if (NULL != jtTableCreate.rgcolumncreate) + { + // Don't record the HRESULT here or it will override the return value of this function + FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); + } + + if (fTransaction) + { + EseCommitTransaction(jsSession); + } + + return hr; +} + +// Todo: support overwrite flag? Unfortunately, requires WinXP and up +// 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 +HRESULT DAPI EseEnsureDatabase( + __in JET_SESID jsSession, + __in_z LPCWSTR pszFile, + __in ESE_DATABASE_SCHEMA *pdsSchema, + __out JET_DBID* pjdbDb, + __in BOOL fExclusive, + __in BOOL fReadonly + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jgrOptions = 0; + LPWSTR pszDir = NULL; + LPSTR pszAnsiFile = NULL; + + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, + // likely breaking all those with unicode characters in their path. + hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting database name to ansi"); + + hr = PathGetDirectory(pszFile, &pszDir); + EseExitOnFailure(hr, "Failed to get directory that will contain database file"); + + hr = DirEnsureExists(pszDir, NULL); + EseExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir); + + if (FileExistsEx(pszFile, NULL)) + { + if (fReadonly) + { + jgrOptions = jgrOptions | JET_bitDbReadOnly; + } + + jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile); + + // This flag doesn't apply to attach, only applies to Open, so only set it after the attach + if (fExclusive) + { + jgrOptions = jgrOptions | JET_bitDbExclusive; + } + + jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile); + } + else + { + jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0); + ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile); + } + + hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema); + EseExitOnFailure(hr, "Failed to ensure database schema matches expectations"); + +LExit: + ReleaseStr(pszDir); + ReleaseStr(pszAnsiFile); + + return hr; +} + +HRESULT DAPI EseCloseDatabase( + __in JET_SESID jsSession, + __in JET_DBID jdbDb + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jgrOptions = 0; + + jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to close database"); + +LExit: + return hr; +} + +HRESULT DAPI EseCreateTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiTable = NULL; + + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting table name to ansi"); + + jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable); + ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable); + +LExit: + ReleaseStr(pszAnsiTable); + + return hr; +} + +HRESULT DAPI EseOpenTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiTable = NULL; + + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting table name to ansi"); + + jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable); + ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable); + +LExit: + ReleaseStr(pszAnsiTable); + + return hr; +} + +HRESULT DAPI EseCloseTable( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetCloseTable(jsSession, jtTable); + ExitOnJetFailure(jEr, hr, "Failed to close table"); + +LExit: + return hr; +} + +HRESULT DAPI EseEnsureColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __in JET_COLTYP jcColumnType, + __in ULONG ulColumnSize, + __in BOOL fFixed, + __in BOOL fNullable, + __out_opt JET_COLUMNID *pjcColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiColumnName = NULL; + JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) }; + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; + + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting column name to ansi"); + + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); + if (JET_errSuccess == jEr) + { + // Return the found columnID + if (NULL != pjcColumn) + { + *pjcColumn = jcdTempBase.columnid; + } + + ExitFunction1(hr = S_OK); + } + else if (JET_errColumnNotFound == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); + + jcdColumnDef.columnid = 0; + jcdColumnDef.coltyp = jcColumnType; + jcdColumnDef.wCountry = 0; + jcdColumnDef.langid = 0; + jcdColumnDef.cp = 1200; + jcdColumnDef.wCollate = 0; + jcdColumnDef.cbMax = ulColumnSize; + jcdColumnDef.grbit = 0; + + if (fFixed) + { + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed; + } + if (!fNullable) + { + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL; + } + + jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn); + ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName); + +LExit: + ReleaseStr(pszAnsiColumnName); + + return hr; +} + +HRESULT DAPI EseGetColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __out JET_COLUMNID *pjcColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiColumnName = NULL; + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; + + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting column name to ansi"); + + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); + if (JET_errSuccess == jEr) + { + // Return the found columnID + if (NULL != pjcColumn) + { + *pjcColumn = jcdTempBase.columnid; + } + + ExitFunction1(hr = S_OK); + } + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); + +LExit: + ReleaseStr(pszAnsiColumnName); + + return hr; +} + +HRESULT DAPI EseMoveCursor( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in LONG lRow + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetMove(jsSession, jtTable, lRow, 0); + ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow); + +LExit: + return hr; +} + +HRESULT DAPI EseDeleteRow( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetDelete(jsSession, jtTable); + ExitOnJetFailure(jEr, hr, "Failed to delete row"); + +LExit: + return hr; +} + +HRESULT DAPI EseBeginTransaction( + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetBeginTransaction(jsSession); + ExitOnJetFailure(jEr, hr, "Failed to begin transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EseRollbackTransaction( + __in JET_SESID jsSession, + __in BOOL fAll + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0); + ExitOnJetFailure(jEr, hr, "Failed to rollback transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EseCommitTransaction( + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetCommitTransaction(jsSession, 0); + ExitOnJetFailure(jEr, hr, "Failed to commit transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EsePrepareUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ULONG ulPrep + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep); + ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep); + +LExit: + return hr; +} + +HRESULT DAPI EseFinishUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in BOOL fSeekToInsertedRecord + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + unsigned char rgbBookmark[JET_cbBookmarkMost + 1]; + DWORD cbBookmark; + + if (fSeekToInsertedRecord) + { + jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark); + ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark"); + + jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark); + ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark"); + } + else + { + jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)"); + } + +LExit: + // 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 + if (FAILED(hr)) + { + JetPrepareUpdate(jsSession, jtTable, JET_prepCancel); + } + + return hr; +} + +HRESULT DAPI EseSetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast(cbBuffer), 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in BOOL fValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BYTE bValue = fValue ? 0xFF : 0x00; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_z LPCWSTR pwzValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + size_t cchValue = 0; + ULONG cbValueSize = 0; + + if (pwzValue) + { + hr = ::StringCchLengthW(pwzValue, STRSAFE_MAX_LENGTH, &cchValue); + EseExitOnRootFailure(hr, "Failed to get string length: %ls", pwzValue); + } + + cbValueSize = static_cast((cchValue + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnEmpty( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + ULONG ulActualSize = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); + if (JET_wrnBufferTruncated == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record"); + + if (NULL == *ppbBuffer) + { + *ppbBuffer = reinterpret_cast(MemAlloc(ulActualSize, FALSE)); + EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column"); + } + else + { + *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, ulActualSize, FALSE)); + EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column"); + } + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record"); + + *piBuffer = static_cast(ulActualSize); + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(*ppbBuffer); + } + + return hr; +} + +HRESULT DAPI EseGetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out DWORD *pdwValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record"); + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out BOOL *pfValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BYTE bValue = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record"); + + if (bValue == 0) + { + *pfValue = FALSE; + } + else + { + *pfValue = TRUE; + } + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out LPWSTR *ppszValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + ULONG ulActualSize = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); + if (JET_wrnBufferTruncated == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record"); + + hr = StrAlloc(ppszValue, ulActualSize); + EseExitOnFailure(hr, "Failed to allocate string while retrieving column value"); + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record"); + +LExit: + return hr; +} + +HRESULT DAPI EseBeginQuery( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ESE_QUERY_TYPE qtQueryType, + __out ESE_QUERY_HANDLE *peqhHandle + ) +{ + UNREFERENCED_PARAMETER(jsSession); + UNREFERENCED_PARAMETER(jtTable); + + HRESULT hr = S_OK; + + *peqhHandle = static_cast(MemAlloc(sizeof(ESE_QUERY), TRUE)); + EseExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query"); + + ESE_QUERY *peqHandle = static_cast(*peqhHandle); + peqHandle->qtQueryType = qtQueryType; + peqHandle->jsSession = jsSession; + peqHandle->jtTable = jtTable; + +LExit: + return hr; +} + +// Utility function used by other functions to set a query column +HRESULT DAPI SetQueryColumn( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbData) const void *pvData, + __in DWORD cbData, + __in JET_GRBIT jGrb + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (peqHandle->dwColumns == countof(peqHandle->pvData)) + { + hr = E_NOTIMPL; + EseExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData)); + } + + if (0 == peqHandle->dwColumns) // If it's the first column, start a new key + { + jGrb = jGrb | JET_bitNewKey; + } + + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb); + ExitOnJetFailure(jEr, hr, "Failed to begin new query"); + + // If the query is wildcard, setup the cached copy of pvData + if (ESE_QUERY_EXACT != peqHandle->qtQueryType) + { + peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE); + EseExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory"); + + memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData); + + peqHandle->cbData[peqHandle->dwColumns] = cbData; + } + + // Increment the number of total columns + ++peqHandle->dwColumns; + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnBinary( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ) +{ + HRESULT hr = S_OK; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (cbBuffer > DWORD_MAX) + { + ExitFunction1(hr = E_INVALIDARG); + } + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, reinterpret_cast(pbBuffer), static_cast(cbBuffer), jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as binary) to:"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnDword( + __in ESE_QUERY_HANDLE eqhHandle, + __in DWORD dwData, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnBool( + __in ESE_QUERY_HANDLE eqhHandle, + __in BOOL fValue, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + BYTE bByte = fValue ? 0xFF : 0x00; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnString( + __in ESE_QUERY_HANDLE eqhHandle, + __in_z LPCWSTR pszString, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + DWORD dwStringSize = 0; + size_t cchString = 0; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (pszString) + { + hr = ::StringCchLengthW(pszString, STRSAFE_MAX_LENGTH, &cchString); + EseExitOnRootFailure(hr, "Failed to get size of column string"); + } + + dwStringSize = static_cast(sizeof(WCHAR) * (cchString + 1)); // Add 1 for null terminator + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString); + +LExit: + return hr; +} + +HRESULT DAPI EseFinishQuery( + __in ESE_QUERY_HANDLE eqhHandle + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (peqHandle->fIndexRangeSet) + { + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove); + ExitOnJetFailure(jEr, hr, "Failed to release index range"); + + peqHandle->fIndexRangeSet = FALSE; + } + + for (int i=0; i < countof(peqHandle->pvData); ++i) + { + ReleaseMem(peqHandle->pvData[i]); + } + + ReleaseMem(peqHandle); + +LExit: + return hr; +} + +HRESULT DAPI EseRunQuery( + __in ESE_QUERY_HANDLE eqhHandle + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jGrb = 0; + JET_GRBIT jGrbSeekType = 0; + DWORD i; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (ESE_QUERY_EXACT == peqHandle->qtQueryType) + { + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ); + ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table"); + } + else + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrbSeekType = JET_bitSeekGE; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrbSeekType = JET_bitSeekLE; + } + + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType); + if (jEr == JET_wrnSeekNotEqual) + { + jEr = JET_errSuccess; + } + + // At this point we've already set our cursor to the beginning of the range of records to select. + // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange() + // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx + for (i = 0; i < peqHandle->dwColumns; ++i) + { + if (i == 0) + { + jGrb = JET_bitNewKey; + } + else + { + jGrb = 0; + } + + // On the last iteration + if (i == peqHandle->dwColumns - 1) + { + jGrb |= JET_bitFullColumnEndLimit; + } + + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb); + ExitOnJetFailure(jEr, hr, "Failed to begin new query"); + } + + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit); + ExitOnJetFailure(jEr, hr, "Failed to set index range"); + + peqHandle->fIndexRangeSet = TRUE; + + // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does) + // So, let's check if there is a current record before returning (by reading the first byte of one). + jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0); + ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query"); + } + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__) +#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__) + +// constants + +const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; +const BYTE UTF16BOM[] = {0xFF, 0xFE}; + +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; + +/******************************************************************* + FileFromPath - returns a pointer to the file part of the path + +********************************************************************/ +extern "C" LPWSTR DAPI FileFromPath( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + return NULL; + + LPWSTR wzFile = const_cast(wzPath); + for (LPWSTR wz = wzFile; *wz; ++wz) + { + // valid delineators + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || L':' == *wz) + wzFile = wz + 1; + } + + return wzFile; +} + + +/******************************************************************* + FileResolvePath - gets the full path to a file resolving environment + variables along the way. + +********************************************************************/ +extern "C" HRESULT DAPI FileResolvePath( + __in_z LPCWSTR wzRelativePath, + __out LPWSTR *ppwzFullPath + ) +{ + Assert(wzRelativePath && *wzRelativePath); + + HRESULT hr = S_OK; + DWORD cch = 0; + LPWSTR pwzExpandedPath = NULL; + DWORD cchExpandedPath = 0; + + LPWSTR pwzFullPath = NULL; + DWORD cchFullPath = 0; + + LPWSTR wzFileName = NULL; + + // + // First, expand any environment variables. + // + cchExpandedPath = MAX_PATH; + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); + FileExitOnFailure(hr, "Failed to allocate space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + cchExpandedPath = cch; + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); + FileExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + FileExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + } + } + + // + // Second, get the full path. + // + cchFullPath = MAX_PATH; + hr = StrAlloc(&pwzFullPath, cchFullPath); + FileExitOnFailure(hr, "Failed to allocate space for full path."); + + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + } + else if (cchFullPath < cch) + { + cchFullPath = cch; + hr = StrAlloc(&pwzFullPath, cchFullPath); + FileExitOnFailure(hr, "Failed to re-allocate more space for full path."); + + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + } + else if (cchFullPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + FileExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + } + } + + *ppwzFullPath = pwzFullPath; + pwzFullPath = NULL; + +LExit: + ReleaseStr(pwzFullPath); + ReleaseStr(pwzExpandedPath); + + return hr; +} + + +/******************************************************************* +FileStripExtension - Strip extension from filename +********************************************************************/ +extern "C" HRESULT DAPI FileStripExtension( +__in_z LPCWSTR wzFileName, +__out LPWSTR *ppwzFileNameNoExtension +) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + size_t cchFileName = 0; + LPWSTR pwzFileNameNoExtension = NULL; + size_t cchFileNameNoExtension = 0; + errno_t err = 0; + + hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_LENGTH, &cchFileName); + FileExitOnRootFailure(hr, "failed to get length of file name: %ls", wzFileName); + + cchFileNameNoExtension = cchFileName + 1; + + hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); + FileExitOnFailure(hr, "failed to allocate space for File Name without extension"); + + // _wsplitpath_s can handle drive/path/filename/extension + err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); + if (err) + { + hr = E_INVALIDARG; + FileExitOnRootFailure(hr, "failed to parse filename: '%ls', error: %d", wzFileName, err); + } + + *ppwzFileNameNoExtension = pwzFileNameNoExtension; + pwzFileNameNoExtension = NULL; + +LExit: + ReleaseStr(pwzFileNameNoExtension); + + return hr; +} + + +/******************************************************************* +FileChangeExtension - Changes the extension of a filename +********************************************************************/ +extern "C" HRESULT DAPI FileChangeExtension( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzNewExtension, + __out LPWSTR *ppwzFileNameNewExtension + ) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + LPWSTR sczFileName = NULL; + + hr = FileStripExtension(wzFileName, &sczFileName); + FileExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); + + hr = StrAllocConcat(&sczFileName, wzNewExtension, 0); + FileExitOnFailure(hr, "Failed to add new extension."); + + *ppwzFileNameNewExtension = sczFileName; + sczFileName = NULL; + +LExit: + ReleaseStr(sczFileName); + + return hr; +} + + +/******************************************************************* +FileAddSuffixToBaseName - Adds a suffix the base portion of a file +name; e.g., file.ext to fileSuffix.ext. +********************************************************************/ +extern "C" HRESULT DAPI FileAddSuffixToBaseName( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzSuffix, + __out_z LPWSTR* psczNewFileName + ) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + LPWSTR sczNewFileName = NULL; + size_t cchFileName = 0; + + hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_CCH, &cchFileName); + FileExitOnRootFailure(hr, "Failed to get length of file name: %ls", wzFileName); + + LPCWSTR wzExtension = wzFileName + cchFileName; + while (wzFileName < wzExtension && L'.' != *wzExtension) + { + --wzExtension; + } + + if (wzFileName < wzExtension) + { + // found an extension so add the suffix before it + hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension); + } + else + { + // no extension, so add the suffix at the end of the whole name + hr = StrAllocString(&sczNewFileName, wzFileName, 0); + FileExitOnFailure(hr, "Failed to allocate new file name."); + + hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0); + } + FileExitOnFailure(hr, "Failed to allocate new file name with suffix."); + + *psczNewFileName = sczNewFileName; + sczNewFileName = NULL; + +LExit: + ReleaseStr(sczNewFileName); + + return hr; +} + + +/******************************************************************* + FileVersion + +********************************************************************/ +extern "C" HRESULT DAPI FileVersion( + __in_z LPCWSTR wzFilename, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ) +{ + HRESULT hr = S_OK; + + DWORD dwHandle = 0; + UINT cbVerBuffer = 0; + LPVOID pVerBuffer = NULL; + VS_FIXEDFILEINFO* pvsFileInfo = NULL; + UINT cbFileInfo = 0; + + if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle))) + { + FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + } + + pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer); + FileExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); + + if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + } + + if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); + } + + *pdwVerMajor = pvsFileInfo->dwFileVersionMS; + *pdwVerMinor = pvsFileInfo->dwFileVersionLS; + +LExit: + if (pVerBuffer) + { + ::GlobalFree(pVerBuffer); + } + return hr; +} + + +/******************************************************************* + FileVersionFromString + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionFromString( + __in_z LPCWSTR wzVersion, + __out DWORD* pdwVerMajor, + __out DWORD* pdwVerMinor + ) +{ + Assert(pdwVerMajor && pdwVerMinor); + + HRESULT hr = S_OK; + LPCWSTR pwz = wzVersion; + DWORD dw; + + *pdwVerMajor = 0; + *pdwVerMinor = 0; + + if ((L'v' == *pwz) || (L'V' == *pwz)) + { + ++pwz; + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMajor = dw << 16; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMajor |= dw; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMinor = dw << 16; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && L'\0' == *pwz && dw < 0x10000) + { + *pdwVerMinor |= dw; + } + else + { + ExitFunction1(hr = S_FALSE); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileVersionFromStringEx + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionFromStringEx( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __out DWORD64* pqwVersion + ) +{ + Assert(wzVersion); + Assert(pqwVersion); + + HRESULT hr = S_OK; + LPCWSTR wzEnd = NULL; + LPCWSTR wzPartBegin = wzVersion; + LPCWSTR wzPartEnd = wzVersion; + DWORD iPart = 0; + USHORT us = 0; + DWORD64 qwVersion = 0; + + // get string length if not provided + if (0 >= cchVersion) + { + hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); + FileExitOnRootFailure(hr, "Failed to get length of file version string: %ls", wzVersion); + + if (0 >= cchVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + if ((L'v' == *wzVersion) || (L'V' == *wzVersion)) + { + ++wzVersion; + --cchVersion; + wzPartBegin = wzVersion; + wzPartEnd = wzVersion; + } + + // save end pointer + wzEnd = wzVersion + cchVersion; + + // loop through parts + for (;;) + { + if (4 <= iPart) + { + // error, too many parts + ExitFunction1(hr = E_INVALIDARG); + } + + // find end of part + while (wzPartEnd < wzEnd && L'.' != *wzPartEnd) + { + ++wzPartEnd; + } + if (wzPartBegin == wzPartEnd) + { + // error, empty part + ExitFunction1(hr = E_INVALIDARG); + } + + DWORD cchPart; + hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); + FileExitOnFailure(hr, "Version number part was too long."); + + // parse version part + hr = StrStringToUInt16(wzPartBegin, cchPart, &us); + FileExitOnFailure(hr, "Failed to parse version number part."); + + // add part to qword version + qwVersion |= (DWORD64)us << ((3 - iPart) * 16); + + if (wzPartEnd >= wzEnd) + { + // end of string + break; + } + + wzPartBegin = ++wzPartEnd; // skip over separator + ++iPart; + } + + *pqwVersion = qwVersion; + +LExit: + return hr; +} + +/******************************************************************* + FileVersionFromStringEx - Formats the DWORD64 as a string version. + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionToStringEx( + __in DWORD64 qwVersion, + __out LPWSTR* psczVersion + ) +{ + HRESULT hr = S_OK; + WORD wMajor = 0; + WORD wMinor = 0; + WORD wBuild = 0; + WORD wRevision = 0; + + // Mask and shift each WORD for each field. + wMajor = (WORD)(qwVersion >> 48 & 0xffff); + wMinor = (WORD)(qwVersion >> 32 & 0xffff); + wBuild = (WORD)(qwVersion >> 16 & 0xffff); + wRevision = (WORD)(qwVersion & 0xffff); + + // Format and return the version string. + hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision); + FileExitOnFailure(hr, "Failed to allocate and format the version number."); + +LExit: + return hr; +} + +/******************************************************************* + FileSetPointer - sets the file pointer. + +********************************************************************/ +extern "C" HRESULT DAPI FileSetPointer( + __in HANDLE hFile, + __in DWORD64 dw64Move, + __out_opt DWORD64* pdw64NewPosition, + __in DWORD dwMoveMethod + ) +{ + Assert(INVALID_HANDLE_VALUE != hFile); + + HRESULT hr = S_OK; + LARGE_INTEGER liMove; + LARGE_INTEGER liNewPosition; + + liMove.QuadPart = dw64Move; + if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) + { + FileExitWithLastError(hr, "Failed to set file pointer."); + } + + if (pdw64NewPosition) + { + *pdw64NewPosition = liNewPosition.QuadPart; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileSize + +********************************************************************/ +extern "C" HRESULT DAPI FileSize( + __in_z LPCWSTR pwzFileName, + __out LONGLONG* pllSize + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + FileExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); + + hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (INVALID_HANDLE_VALUE == hFile) + { + FileExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); + } + + hr = FileSizeByHandle(hFile, pllSize); + FileExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + + +/******************************************************************* + FileSizeByHandle + +********************************************************************/ +extern "C" HRESULT DAPI FileSizeByHandle( + __in HANDLE hFile, + __out LONGLONG* pllSize + ) +{ + Assert(INVALID_HANDLE_VALUE != hFile && pllSize); + HRESULT hr = S_OK; + LARGE_INTEGER li; + + *pllSize = 0; + + if (!::GetFileSizeEx(hFile, &li)) + { + FileExitWithLastError(hr, "Failed to get size of file."); + } + + *pllSize = li.QuadPart; + +LExit: + return hr; +} + + +/******************************************************************* + FileExistsEx + +********************************************************************/ +extern "C" BOOL DAPI FileExistsEx( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + Assert(wzPath && *wzPath); + BOOL fExists = FALSE; + + WIN32_FIND_DATAW fd = { }; + HANDLE hff; + + if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd))) + { + ::FindClose(hff); + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + if (pdwAttributes) + { + *pdwAttributes = fd.dwFileAttributes; + } + + fExists = TRUE; + } + } + + return fExists; +} + + +/******************************************************************* + FileExistsAfterRestart - checks that a file exists and will continue + to exist after restart. + +********************************************************************/ +extern "C" BOOL DAPI FileExistsAfterRestart( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + HRESULT hr = S_OK; + BOOL fExists = FALSE; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + int nCompare = 0; + + fExists = FileExistsEx(wzPath, pdwAttributes); + if (fExists) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + // Skip the long path designator if present. + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) + { + wzRename += 4; + } + + hr = PathCompare(wzPath, wzRename, &nCompare); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + if (CSTR_EQUAL == nCompare) + { + fExists = FALSE; + break; + } + } + } + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return fExists; +} + + +/******************************************************************* + FileRemoveFromPendingRename - removes the file path from the pending + file rename list. + +********************************************************************/ +extern "C" HRESULT DAPI FileRemoveFromPendingRename( + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + int nCompare = 0; + BOOL fRemoved = FALSE; + DWORD cNewRenames = 0; + + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + // Skip the long path designator if present. + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) + { + wzRename += 4; + } + + hr = PathCompare(wzPath, wzRename, &nCompare); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + // If we find our path in the list, null out the source and target slot and + // we'll compact the array next. + if (CSTR_EQUAL == nCompare) + { + ReleaseNullStr(rgsczRenames[i]); + ReleaseNullStr(rgsczRenames[i + 1]); + fRemoved = TRUE; + } + } + } + + if (fRemoved) + { + // Compact the array by removing any nulls. + for (DWORD i = 0; i < cRenames; ++i) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename) + { + rgsczRenames[cNewRenames] = wzRename; + ++cNewRenames; + } + } + + cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. + + // Write the new array back to the pending file rename key. + hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); + FileExitOnFailure(hr, "Failed to update pending file renames."); + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return hr; +} + + +/******************************************************************* + FileRead - read a file into memory + +********************************************************************/ +extern "C" HRESULT DAPI FileRead( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath + ) +{ + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE); + return hr; +} + +/******************************************************************* + FileRead - read a file into memory with specified share mode + +********************************************************************/ +extern "C" HRESULT DAPI FileReadEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD dwShareMode + ) +{ + HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode); + return hr; +} + +/******************************************************************* + FileReadUntil - read a file into memory with a maximum size + +********************************************************************/ +extern "C" HRESULT DAPI FileReadUntil( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD cbMaxRead + ) +{ + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE); + return hr; +} + + +/******************************************************************* + FileReadPartial - read a portion of a file into memory + +********************************************************************/ +extern "C" HRESULT DAPI FileReadPartial( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK + ) +{ + return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE); +} + +/******************************************************************* + FileReadPartial - read a portion of a file into memory + (with specified share mode) +********************************************************************/ +extern "C" HRESULT DAPI FileReadPartialEx( + __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK, + __in DWORD dwShareMode + ) +{ + HRESULT hr = S_OK; + + UINT er = ERROR_SUCCESS; + HANDLE hFile = INVALID_HANDLE_VALUE; + LARGE_INTEGER liFileSize = { }; + DWORD cbData = 0; + BYTE* pbData = NULL; + + FileExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); + FileExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); + FileExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); + FileExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); + + hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + er = ::GetLastError(); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + FileExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); + } + + if (!::GetFileSizeEx(hFile, &liFileSize)) + { + FileExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); + } + + if (fSeek) + { + if (cbStartPosition > liFileSize.QuadPart) + { + hr = E_INVALIDARG; + FileExitOnFailure(hr, "Start position %d bigger than file '%ls' size %llu", cbStartPosition, wzSrcPath, liFileSize.QuadPart); + } + + DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT); + if (INVALID_SET_FILE_POINTER == dwErr) + { + FileExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); + } + } + else + { + cbStartPosition = 0; + } + + if (fPartialOK) + { + cbData = cbMaxRead; + } + else + { + cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD + if (cbMaxRead < liFileSize.QuadPart - cbStartPosition) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + FileExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); + } + } + + if (*ppbDest) + { + if (0 == cbData) + { + ReleaseNullMem(*ppbDest); + *pcbDest = 0; + ExitFunction1(hr = S_OK); + } + + LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE); + FileExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); + + pbData = static_cast(pv); + } + else + { + if (0 == cbData) + { + *pcbDest = 0; + ExitFunction1(hr = S_OK); + } + + pbData = static_cast(MemAlloc(cbData, TRUE)); + FileExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); + } + + DWORD cbTotalRead = 0; + DWORD cbRead = 0; + do + { + DWORD cbRemaining = 0; + hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining); + FileExitOnFailure(hr, "Underflow calculating remaining buffer size."); + + if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); + } + + cbTotalRead += cbRead; + } while (cbRead); + + if (cbTotalRead != cbData) + { + hr = E_UNEXPECTED; + FileExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); + } + + *ppbDest = pbData; + pbData = NULL; + *pcbDest = cbData; + +LExit: + ReleaseMem(pbData); + ReleaseFile(hFile); + + return hr; +} + +extern "C" HRESULT DAPI FileReadHandle( + __in HANDLE hFile, + __in_bcount(cbDest) LPBYTE pbDest, + __in SIZE_T cbDest + ) +{ + HRESULT hr = 0; + DWORD cbDataRead = 0; + SIZE_T cbRemaining = cbDest; + SIZE_T cbTotal = 0; + + while (0 < cbRemaining) + { + if (!::ReadFile(hFile, pbDest + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataRead, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_MORE_DATA == er) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + FileExitOnRootFailure(hr, "Failed to read data from file handle."); + } + + cbRemaining -= cbDataRead; + cbTotal += cbDataRead; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileWrite - write a file from memory + +********************************************************************/ +extern "C" HRESULT DAPI FileWrite( + __in_z LPCWSTR pwzFileName, + __in DWORD dwFlagsAndAttributes, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData, + __out_opt HANDLE* pHandle + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Open the file + hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); + + hr = FileWriteHandle(hFile, pbData, cbData); + FileExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); + + if (pHandle) + { + *pHandle = hFile; + hFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hFile); + + return hr; +} + + +/******************************************************************* + FileWriteHandle - write to a file handle from memory + +********************************************************************/ +extern "C" HRESULT DAPI FileWriteHandle( + __in HANDLE hFile, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + DWORD cbDataWritten = 0; + SIZE_T cbTotal = 0; + SIZE_T cbRemaining = cbData; + + // Write out all of the data. + while (0 < cbRemaining) + { + if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataWritten, NULL)) + { + FileExitOnLastError(hr, "Failed to write data to file handle."); + } + + cbRemaining -= cbDataWritten; + cbTotal += cbDataWritten; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileCopyUsingHandles + +*******************************************************************/ +extern "C" HRESULT DAPI FileCopyUsingHandles( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __out_opt DWORD64* pcbCopied + ) +{ + HRESULT hr = S_OK; + DWORD64 cbTotalCopied = 0; + BYTE rgbData[4 * 1024]; + DWORD cbRead = 0; + + do + { + cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); + if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read from source."); + } + + if (cbRead) + { + hr = FileWriteHandle(hTarget, rgbData, cbRead); + FileExitOnFailure(hr, "Failed to write to target."); + } + + cbTotalCopied += cbRead; + } while (cbTotalCopied < cbCopy && 0 != cbRead); + + if (pcbCopied) + { + *pcbCopied = cbTotalCopied; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileCopyUsingHandlesWithProgress + +*******************************************************************/ +extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + __in_opt LPVOID lpData + ) +{ + HRESULT hr = S_OK; + DWORD64 cbTotalCopied = 0; + BYTE rgbData[64 * 1024]; + DWORD cbRead = 0; + + LARGE_INTEGER liSourceSize = { }; + LARGE_INTEGER liTotalCopied = { }; + LARGE_INTEGER liZero = { }; + DWORD dwResult = 0; + + hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart); + FileExitOnFailure(hr, "Failed to get size of source."); + + if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart) + { + liSourceSize.QuadPart = cbCopy; + } + + if (lpProgressRoutine) + { + dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData); + switch (dwResult) + { + case PROGRESS_CONTINUE: + break; + + case PROGRESS_CANCEL: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_STOP: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_QUIET: + lpProgressRoutine = NULL; + break; + } + } + + // Set size of the target file. + ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN); + + if (!::SetEndOfFile(hTarget)) + { + FileExitWithLastError(hr, "Failed to set end of target file."); + } + + if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN)) + { + FileExitWithLastError(hr, "Failed to reset target file pointer."); + } + + // Copy with progress. + while (0 == cbCopy || cbTotalCopied < cbCopy) + { + cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); + if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read from source."); + } + + if (cbRead) + { + hr = FileWriteHandle(hTarget, rgbData, cbRead); + FileExitOnFailure(hr, "Failed to write to target."); + + cbTotalCopied += cbRead; + + if (lpProgressRoutine) + { + liTotalCopied.QuadPart = cbTotalCopied; + dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData); + switch (dwResult) + { + case PROGRESS_CONTINUE: + break; + + case PROGRESS_CANCEL: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_STOP: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_QUIET: + lpProgressRoutine = NULL; + break; + } + } + } + else + { + break; + } + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureCopy + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureCopy( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite + ) +{ + HRESULT hr = S_OK; + DWORD er; + + // try to copy the file first + if (::CopyFileW(wzSource, wzTarget, !fOverwrite)) + { + ExitFunction(); // we're done + } + + er = ::GetLastError(); // check the error and do the right thing below + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) + { + // if not overwriting this is an expected error + ExitFunction1(hr = S_FALSE); + } + else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist + { + // try to create the directory then do the copy + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + if (pwzLastSlash) + { + *pwzLastSlash = L'\0'; // null terminate + hr = DirEnsureExists(wzTarget, NULL); + *pwzLastSlash = L'\\'; // now put the slash back + FileExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); + + // try to copy again + if (!::CopyFileW(wzSource, wzTarget, fOverwrite)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); + } + } + else // no path was specified so just return the error + { + hr = HRESULT_FROM_WIN32(er); + } + } + else // unexpected error + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureCopyWithRetry + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureCopyWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); + + HRESULT hr = E_FAIL; + DWORD i = 0; + + for (i = 0; FAILED(hr) && i <= cRetry; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr + || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr) + { + break; // no reason to retry these errors. + } + } + FileExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureMove + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureMove( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy + ) +{ + HRESULT hr = S_OK; + DWORD er; + + DWORD dwFlags = 0; + + if (fOverwrite) + { + dwFlags |= MOVEFILE_REPLACE_EXISTING; + } + if (fAllowCopy) + { + dwFlags |= MOVEFILE_COPY_ALLOWED; + } + + // try to move the file first + if (::MoveFileExW(wzSource, wzTarget, dwFlags)) + { + ExitFunction(); // we're done + } + + er = ::GetLastError(); // check the error and do the right thing below + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) + { + // if not overwriting this is an expected error + ExitFunction1(hr = S_FALSE); + } + else if (ERROR_FILE_NOT_FOUND == er) + { + // We are seeing some cases where ::MoveFileEx() says a file was not found + // but the source file is actually present. In that case, return path not + // found so we try to create the target path since that is most likely + // what is missing. Otherwise, the source file is missing and we're obviously + // not going to be recovering from that. + if (FileExistsEx(wzSource, NULL)) + { + er = ERROR_PATH_NOT_FOUND; + } + } + + // If the path doesn't exist, try to create the directory tree then do the move. + if (ERROR_PATH_NOT_FOUND == er) + { + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + if (pwzLastSlash) + { + *pwzLastSlash = L'\0'; // null terminate + hr = DirEnsureExists(wzTarget, NULL); + *pwzLastSlash = L'\\'; // now put the slash back + FileExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); + + // try to move again + if (!::MoveFileExW(wzSource, wzTarget, dwFlags)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); + } + } + else // no path was specified so just return the error + { + hr = HRESULT_FROM_WIN32(er); + } + } + else // unexpected error + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureMoveWithRetry + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureMoveWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); + + HRESULT hr = E_FAIL; + DWORD i = 0; + + for (i = 0; FAILED(hr) && i < cRetry + 1; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy); + } + FileExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + +LExit: + return hr; +} + + +/******************************************************************* + FileCreateTemp - creates an empty temp file + + NOTE: uses ANSI functions internally so it is Win9x safe +********************************************************************/ +extern "C" HRESULT DAPI FileCreateTemp( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ) +{ + Assert(wzPrefix && *wzPrefix); + HRESULT hr = S_OK; + LPSTR pszTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + LPSTR pszTempFile = NULL; + + int i = 0; + + hr = StrAnsiAlloc(&pszTempPath, cchTempPath); + FileExitOnFailure(hr, "failed to allocate memory for the temp path"); + ::GetTempPathA(cchTempPath, pszTempPath); + + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension); + FileExitOnFailure(hr, "failed to allocate memory for log file"); + + hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + continue; + } + FileExitOnFailureDebugTrace(hr, "failed to create file: %hs", pszTempFile); + } + } + + if (ppwzTempFile) + { + hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(pszTempFile); + ReleaseStr(pszTempPath); + + return hr; +} + + +/******************************************************************* + FileCreateTempW - creates an empty temp file + +*******************************************************************/ +extern "C" HRESULT DAPI FileCreateTempW( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ) +{ + Assert(wzPrefix && *wzPrefix); + HRESULT hr = E_FAIL; + + WCHAR wzTempPath[MAX_PATH]; + DWORD cchTempPath = countof(wzTempPath); + LPWSTR pwzTempFile = NULL; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + int i = 0; + + if (!::GetTempPathW(cchTempPath, wzTempPath)) + { + FileExitOnLastError(hr, "failed to get temp path"); + } + + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); + FileExitOnFailure(hr, "failed to allocate memory for temp filename"); + + hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + continue; + } + FileExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); + } + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + + if (ppwzTempFile) + { + *ppwzTempFile = pwzTempFile; + pwzTempFile = NULL; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(pwzTempFile); + + return hr; +} + + +/******************************************************************* + FileIsSame + +********************************************************************/ +extern "C" HRESULT DAPI FileIsSame( + __in_z LPCWSTR wzFile1, + __in_z LPCWSTR wzFile2, + __out LPBOOL lpfSameFile + ) +{ + HRESULT hr = S_OK; + HANDLE hFile1 = NULL; + HANDLE hFile2 = NULL; + BY_HANDLE_FILE_INFORMATION fileInfo1 = { }; + BY_HANDLE_FILE_INFORMATION fileInfo2 = { }; + + hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); + + hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); + + if (!::GetFileInformationByHandle(hFile1, &fileInfo1)) + { + FileExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); + } + + if (!::GetFileInformationByHandle(hFile2, &fileInfo2)) + { + FileExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); + } + + *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber && + fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh && + fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE; + +LExit: + ReleaseFile(hFile1); + ReleaseFile(hFile2); + + return hr; +} + +/******************************************************************* + FileEnsureDelete - deletes a file, first removing read-only, + hidden, or system attributes if necessary. +********************************************************************/ +extern "C" HRESULT DAPI FileEnsureDelete( + __in_z LPCWSTR wzFile + ) +{ + HRESULT hr = S_OK; + + DWORD dwAttrib = INVALID_FILE_ATTRIBUTES; + if (FileExistsEx(wzFile, &dwAttrib)) + { + if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM) + { + if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL)) + { + FileExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); + } + } + + if (!::DeleteFileW(wzFile)) + { + FileExitOnLastError(hr, "Failed to delete file: %ls", wzFile); + } + } + +LExit: + return hr; +} + +/******************************************************************* + FileGetTime - Gets the file time of a specified file +********************************************************************/ +extern "C" HRESULT DAPI FileGetTime( + __in_z LPCWSTR wzFile, + __out_opt LPFILETIME lpCreationTime, + __out_opt LPFILETIME lpLastAccessTime, + __out_opt LPFILETIME lpLastWriteTime + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + + hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) + { + FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + +/******************************************************************* + FileSetTime - Sets the file time of a specified file +********************************************************************/ +extern "C" HRESULT DAPI FileSetTime( + __in_z LPCWSTR wzFile, + __in_opt const FILETIME *lpCreationTime, + __in_opt const FILETIME *lpLastAccessTime, + __in_opt const FILETIME *lpLastWriteTime + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) + { + FileExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + +/******************************************************************* + FileReSetTime - ReSets a file's last acess and modified time to the + creation time of the file +********************************************************************/ +extern "C" HRESULT DAPI FileResetTime( + __in_z LPCWSTR wzFile + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + FILETIME ftCreateTime; + + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL)) + { + FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + } + + if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime)) + { + FileExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + + +/******************************************************************* + FileExecutableArchitecture + +*******************************************************************/ +extern "C" HRESULT DAPI FileExecutableArchitecture( + __in_z LPCWSTR wzFile, + __out FILE_ARCHITECTURE *pArchitecture + ) +{ + HRESULT hr = S_OK; + + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD cbRead = 0; + IMAGE_DOS_HEADER DosImageHeader = { }; + IMAGE_NT_HEADERS NtImageHeader = { }; + + hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (hFile == INVALID_HANDLE_VALUE) + { + FileExitWithLastError(hr, "Failed to open file: %ls", wzFile); + } + + if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); + } + + if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + FileExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); + } + + if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN)) + { + FileExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); + } + + if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); + } + + if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + FileExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); + } + + if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem || + IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem || + IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem) + { + switch (NtImageHeader.FileHeader.Machine) + { + case IMAGE_FILE_MACHINE_I386: + *pArchitecture = FILE_ARCHITECTURE_X86; + break; + case IMAGE_FILE_MACHINE_IA64: + *pArchitecture = FILE_ARCHITECTURE_IA64; + break; + case IMAGE_FILE_MACHINE_AMD64: + *pArchitecture = FILE_ARCHITECTURE_X64; + break; + default: + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + break; + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + } + FileExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); + +LExit: + if (hFile != INVALID_HANDLE_VALUE) + { + ::CloseHandle(hFile); + } + + return hr; +} + +/******************************************************************* + FileToString + +*******************************************************************/ +extern "C" HRESULT DAPI FileToString( + __in_z LPCWSTR wzFile, + __out LPWSTR *psczString, + __out_opt FILE_ENCODING *pfeEncoding + ) +{ + HRESULT hr = S_OK; + BYTE *pbFullFileBuffer = NULL; + SIZE_T cbFullFileBuffer = 0; + BOOL fNullCharFound = FALSE; + LPWSTR sczFileText = NULL; + + // Check if the file is ANSI + hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile); + FileExitOnFailure(hr, "Failed to read file: %ls", wzFile); + + if (0 == cbFullFileBuffer) + { + *psczString = NULL; + ExitFunction1(hr = S_OK); + } + + // UTF-8 BOM + if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM))) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM; + } + + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8); + FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); + + *psczString = sczFileText; + sczFileText = NULL; + } + // UTF-16 BOM, little endian (windows regular UTF-16) + else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM))) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM; + } + + hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR)); + FileExitOnFailure(hr, "Failed to allocate copy of string"); + } + // No BOM, let's try to detect + else + { + for (DWORD i = 0; i < cbFullFileBuffer; ++i) + { + if (pbFullFileBuffer[i] == '\0') + { + fNullCharFound = TRUE; + break; + } + } + + if (!fNullCharFound) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF8; + } + + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8); + if (FAILED(hr)) + { + if (E_OUTOFMEMORY == hr) + { + FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); + } + } + else + { + *psczString = sczFileText; + sczFileText = NULL; + } + } + else if (NULL == *psczString) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF16; + } + + hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR)); + FileExitOnFailure(hr, "Failed to allocate copy of string"); + } + } + +LExit: + ReleaseStr(sczFileText); + ReleaseMem(pbFullFileBuffer); + + return hr; +} + +/******************************************************************* + FileFromString + +*******************************************************************/ +extern "C" HRESULT DAPI FileFromString( + __in_z LPCWSTR wzFile, + __in DWORD dwFlagsAndAttributes, + __in_z LPCWSTR sczString, + __in FILE_ENCODING feEncoding + ) +{ + HRESULT hr = S_OK; + LPSTR sczUtf8String = NULL; + BYTE *pbFullFileBuffer = NULL; + const BYTE *pcbFullFileBuffer = NULL; + SIZE_T cbFullFileBuffer = 0; + SIZE_T cbStrLen = 0; + + switch (feEncoding) + { + case FILE_ENCODING_UTF8: + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); + FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + + hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbFullFileBuffer)); + FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); + + pcbFullFileBuffer = reinterpret_cast(sczUtf8String); + break; + case FILE_ENCODING_UTF8_WITH_BOM: + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); + FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + + hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); + + cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; + + pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); + FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + + memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM)); + memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen); + pcbFullFileBuffer = pbFullFileBuffer; + break; + case FILE_ENCODING_UTF16: + hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of string"); + + cbFullFileBuffer = cbStrLen * sizeof(WCHAR); + pcbFullFileBuffer = reinterpret_cast(sczString); + break; + case FILE_ENCODING_UTF16_WITH_BOM: + hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of string"); + + cbStrLen *= sizeof(WCHAR); + cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; + + pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); + FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + + memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM)); + memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen); + pcbFullFileBuffer = pbFullFileBuffer; + break; + } + + hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL); + FileExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); + +LExit: + ReleaseStr(sczUtf8String); + ReleaseMem(pbFullFileBuffer); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace Gdiplus; + + +// Exit macros +#define GdipExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) +#define GdipExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) +#define GdipExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) +#define GdipExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) +#define GdipExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GDIPUTIL, e, x, s, __VA_ARGS__) +#define GdipExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GDIPUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** + GdipInitialize - initializes GDI+. + + Note: pOutput must be non-NULL if pInput->SuppressBackgroundThread + is TRUE. See GdiplusStartup() for more information. +********************************************************************/ +extern "C" HRESULT DAPI GdipInitialize( + __in const Gdiplus::GdiplusStartupInput* pInput, + __out ULONG_PTR* pToken, + __out_opt Gdiplus::GdiplusStartupOutput *pOutput + ) +{ + AssertSz(!pInput->SuppressBackgroundThread || pOutput, "pOutput required if background thread suppressed."); + + HRESULT hr = S_OK; + Status status = Ok; + + status = GdiplusStartup(pToken, pInput, pOutput); + GdipExitOnGdipFailure(status, hr, "Failed to initialize GDI+."); + +LExit: + return hr; +} + +/******************************************************************** + GdipUninitialize - uninitializes GDI+. + +********************************************************************/ +extern "C" void DAPI GdipUninitialize( + __in ULONG_PTR token + ) +{ + GdiplusShutdown(token); +} + +/******************************************************************** + GdipBitmapFromResource - read a GDI+ image out of a resource stream + +********************************************************************/ +extern "C" HRESULT DAPI GdipBitmapFromResource( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szId, + __out Bitmap **ppBitmap + ) +{ + HRESULT hr = S_OK; + LPVOID pvData = NULL; + DWORD cbData = 0; + HGLOBAL hGlobal = NULL;; + LPVOID pv = NULL; + IStream *pStream = NULL; + Bitmap *pBitmap = NULL; + Status gs = Ok; + + hr = ResReadData(hinst, szId, &pvData, &cbData); + GdipExitOnFailure(hr, "Failed to load GDI+ bitmap from resource."); + + // Have to copy the fixed resource data into moveable (heap) memory + // since that's what GDI+ expects. + hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData); + GdipExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory."); + + pv = ::GlobalLock(hGlobal); + GdipExitOnNullWithLastError(pv, hr, "Failed to lock global memory."); + + memcpy(pv, pvData, cbData); + + ::GlobalUnlock(pv); // no point taking any more memory than we have already + pv = NULL; + + hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); + GdipExitOnFailure(hr, "Failed to allocate stream from global memory."); + + hGlobal = NULL; // we gave the global memory to the stream object so it will close it + + pBitmap = Bitmap::FromStream(pStream); + GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream."); + + gs = pBitmap->GetLastStatus(); + GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream."); + + *ppBitmap = pBitmap; + pBitmap = NULL; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + ReleaseObject(pStream); + + if (pv) + { + ::GlobalUnlock(pv); + } + + if (hGlobal) + { + ::GlobalFree(hGlobal); + } + + return hr; +} + + +/******************************************************************** + GdipBitmapFromFile - read a GDI+ image from a file. + +********************************************************************/ +extern "C" HRESULT DAPI GdipBitmapFromFile( + __in_z LPCWSTR wzFileName, + __out Bitmap **ppBitmap + ) +{ + HRESULT hr = S_OK; + Bitmap *pBitmap = NULL; + Status gs = Ok; + + GdipExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName"); + + pBitmap = Bitmap::FromFile(wzFileName); + GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file."); + + gs = pBitmap->GetLastStatus(); + GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName); + + *ppBitmap = pBitmap; + pBitmap = NULL; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + return hr; +} + + +HRESULT DAPI GdipHresultFromStatus( + __in Gdiplus::Status gs + ) +{ + switch (gs) + { + case Ok: + return S_OK; + + case GenericError: + return E_FAIL; + + case InvalidParameter: + return E_INVALIDARG; + + case OutOfMemory: + return E_OUTOFMEMORY; + + case ObjectBusy: + return HRESULT_FROM_WIN32(ERROR_BUSY); + + case InsufficientBuffer: + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + + case NotImplemented: + return E_NOTIMPL; + + case Win32Error: + return E_FAIL; + + case WrongState: + return E_FAIL; + + case Aborted: + return E_ABORT; + + case FileNotFound: + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + case ValueOverflow: + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + case AccessDenied: + return E_ACCESSDENIED; + + case UnknownImageFormat: + return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + + case FontFamilyNotFound: __fallthrough; + case FontStyleNotFound: __fallthrough; + case NotTrueTypeFont: + return E_UNEXPECTED; + + case UnsupportedGdiplusVersion: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + case GdiplusNotInitialized: + return E_UNEXPECTED; + + case PropertyNotFound: __fallthrough; + case PropertyNotSupported: + return E_FAIL; + } + + return E_UNEXPECTED; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define GuidExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) +#define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) +#define GuidExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) +#define GuidExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) +#define GuidExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GUIDUTIL, e, x, s, __VA_ARGS__) +#define GuidExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GUIDUTIL, g, x, s, __VA_ARGS__) + +extern "C" HRESULT DAPI GuidFixedCreate( + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid + ) +{ + HRESULT hr = S_OK; + UUID guid = { }; + + hr = HRESULT_FROM_RPC(::UuidCreate(&guid)); + GuidExitOnFailure(hr, "UuidCreate failed."); + + if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) + { + hr = E_OUTOFMEMORY; + GuidExitOnRootFailure(hr, "Failed to convert guid into string."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI GuidCreate( + __deref_out_z LPWSTR* psczGuid + ) +{ + HRESULT hr = S_OK; + + hr = StrAlloc(psczGuid, GUID_STRING_LENGTH); + GuidExitOnFailure(hr, "Failed to allocate space for guid"); + + hr = GuidFixedCreate(*psczGuid); + GuidExitOnFailure(hr, "Failed to create new guid."); + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include "iis7util.h" + + +// Exit macros +#define IisExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) +#define IisExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) +#define IisExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) +#define IisExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) +#define IisExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_IIS7UTIL, e, x, s, __VA_ARGS__) +#define IisExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_IIS7UTIL, g, x, s, __VA_ARGS__) + +#define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt) + +extern "C" HRESULT DAPI Iis7PutPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT vtPut + ) +{ + HRESULT hr = S_OK; + IAppHostProperty *pProperty = NULL; + BSTR bstrPropName = NULL; + + bstrPropName = ::SysAllocString(wzPropName); + IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pElement->GetPropertyByName(bstrPropName, &pProperty); + IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + + hr = pProperty->put_Value(vtPut); + IisExitOnFailure(hr, "Failed to set property value for %ls", wzPropName); + +LExit: + ReleaseBSTR(bstrPropName); + // caller responsible for cleaning up variant vtPut + ReleaseObject(pProperty); + + return hr; +} + +extern "C" HRESULT DAPI Iis7PutPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_BSTR; + vtPut.bstrVal = ::SysAllocString(wzString); + IisExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut); + +LExit: + ReleaseVariant(vtPut); + + return hr; +} + +extern "C" HRESULT DAPI Iis7PutPropertyInteger( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in DWORD dValue + ) +{ + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_I4; + vtPut.lVal = dValue; + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); +} + +extern "C" HRESULT DAPI Iis7PutPropertyBool( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in BOOL fValue) +{ + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_BOOL; + vtPut.boolVal = (fValue == FALSE) ? VARIANT_FALSE : VARIANT_TRUE; + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); +} + +extern "C" HRESULT DAPI Iis7GetPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT* vtGet + ) +{ + HRESULT hr = S_OK; + IAppHostProperty *pProperty = NULL; + BSTR bstrPropName = NULL; + + bstrPropName = ::SysAllocString(wzPropName); + IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pElement->GetPropertyByName(bstrPropName, &pProperty); + IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + + hr = pProperty->get_Value(vtGet); + IisExitOnFailure(hr, "Failed to get property value for %ls", wzPropName); + +LExit: + ReleaseBSTR(bstrPropName); + // caller responsible for cleaning up variant vtGet + ReleaseObject(pProperty); + + return hr; +} + +extern "C" HRESULT DAPI Iis7GetPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPWSTR* psczGet + ) +{ + HRESULT hr = S_OK; + VARIANT vtGet; + + ::VariantInit(&vtGet); + hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet); + IisExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName); + + if (!ISSTRINGVARIANT(vtGet.vt)) + { + hr = E_UNEXPECTED; + IisExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt); + } + + hr = StrAllocString(psczGet, vtGet.bstrVal, 0); + +LExit: + ReleaseVariant(vtGet); + + return hr; +} + +BOOL DAPI CompareVariantDefault( + __in VARIANT* pVariant1, + __in VARIANT* pVariant2 + ) +{ + BOOL fEqual = FALSE; + + switch (pVariant1->vt) + { + // VarCmp doesn't work for unsigned ints + // We'd like to allow signed/unsigned comparison as well since + // IIS doesn't document variant type for integer fields + case VT_I1: + case VT_UI1: + if (VT_I1 == pVariant2->vt || VT_UI1 == pVariant2->vt) + { + fEqual = pVariant1->bVal == pVariant2->bVal; + } + break; + case VT_I2: + case VT_UI2: + if (VT_I2 == pVariant2->vt || VT_UI2 == pVariant2->vt) + { + fEqual = pVariant1->uiVal == pVariant2->uiVal; + } + break; + case VT_UI4: + case VT_I4: + if (VT_I4 == pVariant2->vt || VT_UI4 == pVariant2->vt) + { + fEqual = pVariant1->ulVal == pVariant2->ulVal; + } + break; + case VT_UI8: + case VT_I8: + if (VT_I8 == pVariant2->vt || VT_UI8 == pVariant2->vt) + { + fEqual = pVariant1->ullVal == pVariant2->ullVal; + } + break; + default: + fEqual = VARCMP_EQ == ::VarCmp(pVariant1, + pVariant2, + LOCALE_INVARIANT, + NORM_IGNORECASE); + } + + return fEqual; +} + +BOOL DAPI CompareVariantPath( + __in VARIANT* pVariant1, + __in VARIANT* pVariant2 + ) +{ + HRESULT hr = S_OK; + BOOL fEqual = FALSE; + LPWSTR wzValue1 = NULL; + LPWSTR wzValue2 = NULL; + + if (ISSTRINGVARIANT(pVariant1->vt)) + { + hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + IisExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal); + } + + if (ISSTRINGVARIANT(pVariant2->vt)) + { + hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + IisExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal); + } + + fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1); + +LExit: + ReleaseNullStr(wzValue1); + ReleaseNullStr(wzValue2); + return fEqual; +} + +BOOL DAPI IsMatchingAppHostElementCallback( + __in IAppHostElement *pElement, + __in_bcount(sizeof(IIS7_APPHOSTELEMENTCOMPARISON)) LPVOID pContext + ) +{ + IIS7_APPHOSTELEMENTCOMPARISON* pComparison = (IIS7_APPHOSTELEMENTCOMPARISON*) pContext; + + return Iis7IsMatchingAppHostElement(pElement, pComparison); +} + +extern "C" BOOL DAPI Iis7IsMatchingAppHostElement( + __in IAppHostElement *pElement, + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison + ) +{ + HRESULT hr = S_OK; + BOOL fResult = FALSE; + IAppHostProperty *pProperty = NULL; + BSTR bstrElementName = NULL; + + VARIANT vPropValue; + ::VariantInit(&vPropValue); + + // Use the default comparator if a comparator is not specified + VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault; + + hr = pElement->get_Name(&bstrElementName); + IisExitOnFailure(hr, "Failed to get name of element"); + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1)) + { + ExitFunction(); + } + + hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue); + IisExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName); + + if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue)) + { + fResult = TRUE; + } + +LExit: + ReleaseBSTR(bstrElementName); + ReleaseVariant(vPropValue); + ReleaseObject(pProperty); + + return fResult; +} + +BOOL DAPI IsMatchingAppHostMethod( + __in IAppHostMethod *pMethod, + __in LPCWSTR wzMethodName + ) +{ + HRESULT hr = S_OK; + BOOL fResult = FALSE; + BSTR bstrName = NULL; + + hr = pMethod->get_Name(&bstrName); + IisExitOnFailure(hr, "Failed to get name of element"); + + Assert(bstrName); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1)) + { + fResult = TRUE; + } + +LExit: + ReleaseBSTR(bstrName); + + return fResult; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementPath( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; + VARIANT vtValue = { }; + ::VariantInit(&vtValue); + + vtValue.vt = VT_BSTR; + vtValue.bstrVal = ::SysAllocString(wzAttributeValue); + IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + comparison.sczElementName = wzElementName; + comparison.sczAttributeName = wzAttributeName; + comparison.pvAttributeValue = &vtValue; + comparison.pComparator = CompareVariantPath; + + hr = Iis7EnumAppHostElements(pCollection, + IsMatchingAppHostElementCallback, + &comparison, + ppElement, + pdwIndex); + +LExit: + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementString( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + VARIANT vtValue; + ::VariantInit(&vtValue); + + vtValue.vt = VT_BSTR; + vtValue.bstrVal = ::SysAllocString(wzAttributeValue); + IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = Iis7FindAppHostElementVariant(pCollection, + wzElementName, + wzAttributeName, + &vtValue, + ppElement, + pdwIndex); + +LExit: + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementInteger( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in DWORD dwAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + VARIANT vtValue; + ::VariantInit(&vtValue); + + vtValue.vt = VT_UI4; + vtValue.ulVal = dwAttributeValue; + + hr = Iis7FindAppHostElementVariant(pCollection, + wzElementName, + wzAttributeName, + &vtValue, + ppElement, + pdwIndex); + + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementVariant( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in VARIANT* pvAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; + comparison.sczElementName = wzElementName; + comparison.sczAttributeName = wzAttributeName; + comparison.pvAttributeValue = pvAttributeValue; + comparison.pComparator = CompareVariantDefault; + + return Iis7EnumAppHostElements(pCollection, + IsMatchingAppHostElementCallback, + &comparison, + ppElement, + pdwIndex); +} + +extern "C" HRESULT DAPI Iis7EnumAppHostElements( + __in IAppHostElementCollection *pCollection, + __in ENUMAPHOSTELEMENTPROC pCallback, + __in LPVOID pContext, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pElement = NULL; + DWORD dwElements = 0; + + VARIANT vtIndex; + ::VariantInit(&vtIndex); + + if (NULL != ppElement) + { + *ppElement = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = MAXDWORD; + } + + hr = pCollection->get_Count(&dwElements); + IisExitOnFailure(hr, "Failed get application IAppHostElementCollection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < dwElements; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pElement); + IisExitOnFailure(hr, "Failed get IAppHostElement element"); + + if (pCallback(pElement, pContext)) + { + if (NULL != ppElement) + { + *ppElement = pElement; + pElement = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = i; + } + break; + } + + ReleaseNullObject(pElement); + } + +LExit: + ReleaseObject(pElement); + ReleaseVariant(vtIndex); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostMethod( + __in IAppHostMethodCollection *pCollection, + __in LPCWSTR wzMethodName, + __out IAppHostMethod** ppMethod, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IAppHostMethod *pMethod = NULL; + DWORD dwMethods = 0; + + VARIANT vtIndex; + ::VariantInit(&vtIndex); + + if (NULL != ppMethod) + { + *ppMethod = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = MAXDWORD; + } + + hr = pCollection->get_Count(&dwMethods); + IisExitOnFailure(hr, "Failed get application IAppHostMethodCollection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < dwMethods; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pMethod); + IisExitOnFailure(hr, "Failed get IAppHostMethod element"); + + if (IsMatchingAppHostMethod(pMethod, wzMethodName)) + { + if (NULL != ppMethod) + { + *ppMethod = pMethod; + pMethod = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = i; + } + break; + } + + ReleaseNullObject(pMethod); + } + +LExit: + ReleaseObject(pMethod); + ReleaseVariant(vtIndex); + + return hr; +} 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#define ReleaseSid(x) if (x) { AclFreeSid(x); } +#define ReleaseNullSid(x) if (x) { AclFreeSid(x); x = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + +// structs +struct ACL_ACCESS +{ + BOOL fDenyAccess; + DWORD dwAccessMask; + + // TODO: consider using a union + LPCWSTR pwzAccountName; // NOTE: the last three items in this structure are ignored if this is not NULL + + SID_IDENTIFIER_AUTHORITY sia; // used if pwzAccountName is NULL + BYTE nSubAuthorityCount; + DWORD nSubAuthority[8]; +}; + +struct ACL_ACE +{ + DWORD dwFlags; + DWORD dwMask; + PSID psid; +}; + + +// functions +HRESULT DAPI AclCheckAccess( + __in HANDLE hToken, + __in ACL_ACCESS* paa + ); +HRESULT DAPI AclCheckAdministratorAccess( + __in HANDLE hToken + ); +HRESULT DAPI AclCheckLocalSystemAccess( + __in HANDLE hToken + ); + +HRESULT DAPI AclGetWellKnownSid( + __in WELL_KNOWN_SID_TYPE wkst, + __deref_out PSID* ppsid + ); +HRESULT DAPI AclGetAccountSid( + __in_opt LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out PSID* ppsid + ); +HRESULT DAPI AclGetAccountSidString( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* ppwzSid + ); + +HRESULT DAPI AclCreateDacl( + __in_ecount(cDeny) ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount(cAllow) ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAcl + ); +HRESULT DAPI AclAddToDacl( + __in ACL* pAcl, + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAclNew + ); +HRESULT DAPI AclMergeDacls( + __in const ACL* pAcl1, + __in const ACL* pAcl2, + __deref_out ACL** ppAclNew + ); +HRESULT DAPI AclCreateDaclOld( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out ACL** ppAcl + ); +HRESULT DAPI AclCreateSecurityDescriptor( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclCreateSecurityDescriptorFromDacl( + __in ACL* pACL, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT __cdecl AclCreateSecurityDescriptorFromString( + __deref_out SECURITY_DESCRIPTOR** ppsd, + __in_z __format_string LPCWSTR wzSddlFormat, + ... + ); +HRESULT DAPI AclDuplicateSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclGetSecurityDescriptor( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclSetSecurityWithRetry( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __in_opt PSID psidOwner, + __in_opt PSID psidGroup, + __in_opt PACL pDacl, + __in_opt PACL pSacl, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); + +HRESULT DAPI AclFreeSid( + __in PSID psid + ); +HRESULT DAPI AclFreeDacl( + __in ACL* pACL + ); +HRESULT DAPI AclFreeSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd + ); + +HRESULT DAPI AclAddAdminToSecurityDescriptor( + __in SECURITY_DESCRIPTOR* pSecurity, + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew + ); + +// Following code in acl2util.cpp due to dependency on crypt32.dll. +HRESULT DAPI AclCalculateServiceSidString( + __in LPCWSTR wzServiceName, + __in SIZE_T cchServiceName, + __deref_out_z LPWSTR* psczSid + ); +HRESULT DAPI AclGetAccountSidStringEx( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* psczSid + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// functions + +/******************************************************************** +AppFreeCommandLineArgs - frees argv from AppParseCommandLine. + +********************************************************************/ +void DAPI AppFreeCommandLineArgs( + __in LPWSTR* argv + ); + +void DAPI AppInitialize( + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], + __in DWORD cSafelyLoadSystemDlls + ); + +/******************************************************************** +AppInitializeUnsafe - initializes without the full standard safety + precautions for an application. + +********************************************************************/ +void DAPI AppInitializeUnsafe(); + +/******************************************************************** +AppParseCommandLine - parses the command line using CommandLineToArgvW. + The caller must free the value of pArgv on success + by calling AppFreeCommandLineArgs. + +********************************************************************/ +DAPI_(HRESULT) AppParseCommandLine( + __in LPCWSTR wzCommandLine, + __in int* argc, + __in LPWSTR** pArgv + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } +#define ReleaseNullApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } + + +const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn"; + +typedef enum APUP_HASH_ALGORITHM +{ + APUP_HASH_ALGORITHM_UNKNOWN, + APUP_HASH_ALGORITHM_MD5, + APUP_HASH_ALGORITHM_SHA1, + APUP_HASH_ALGORITHM_SHA256, + APUP_HASH_ALGORITHM_SHA512, +} APUP_HASH_ALGORITHM; + + +struct APPLICATION_UPDATE_ENCLOSURE +{ + LPWSTR wzUrl; + LPWSTR wzLocalName; + DWORD64 dw64Size; + + BYTE* rgbDigest; + DWORD cbDigest; + APUP_HASH_ALGORITHM digestAlgorithm; + + BOOL fInstaller; +}; + + +struct APPLICATION_UPDATE_ENTRY +{ + LPWSTR wzApplicationId; + LPWSTR wzApplicationType; + LPWSTR wzTitle; + LPWSTR wzSummary; + LPWSTR wzContentType; + LPWSTR wzContent; + + LPWSTR wzUpgradeId; + BOOL fUpgradeExclusive; + VERUTIL_VERSION* pVersion; + VERUTIL_VERSION* pUpgradeVersion; + + DWORD64 dw64TotalSize; + + DWORD cEnclosures; + APPLICATION_UPDATE_ENCLOSURE* rgEnclosures; +}; + + +struct APPLICATION_UPDATE_CHAIN +{ + LPWSTR wzDefaultApplicationId; + LPWSTR wzDefaultApplicationType; + + DWORD cEntries; + APPLICATION_UPDATE_ENTRY* rgEntries; +}; + + +HRESULT DAPI ApupAllocChainFromAtom( + __in ATOM_FEED* pFeed, + __out APPLICATION_UPDATE_CHAIN** ppChain + ); + +HRESULT DAPI ApupFilterChain( + __in APPLICATION_UPDATE_CHAIN* pChain, + __in VERUTIL_VERSION* pVersion, + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain + ); + +void DAPI ApupFreeChain( + __in APPLICATION_UPDATE_CHAIN* pChain + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); } +#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; } + + +struct ATOM_UNKNOWN_ATTRIBUTE +{ + LPWSTR wzNamespace; + LPWSTR wzAttribute; + LPWSTR wzValue; + + ATOM_UNKNOWN_ATTRIBUTE* pNext; +}; + +struct ATOM_UNKNOWN_ELEMENT +{ + LPWSTR wzNamespace; + LPWSTR wzElement; + LPWSTR wzValue; + + ATOM_UNKNOWN_ATTRIBUTE* pAttributes; + ATOM_UNKNOWN_ELEMENT* pNext; +}; + +struct ATOM_LINK +{ + LPWSTR wzRel; + LPWSTR wzTitle; + LPWSTR wzType; + LPWSTR wzUrl; + LPWSTR wzValue; + DWORD64 dw64Length; + + ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttributes; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_CONTENT +{ + LPWSTR wzType; + LPWSTR wzUrl; + LPWSTR wzValue; + + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_AUTHOR +{ + LPWSTR wzName; + LPWSTR wzEmail; + LPWSTR wzUrl; +}; + +struct ATOM_CATEGORY +{ + LPWSTR wzLabel; + LPWSTR wzScheme; + LPWSTR wzTerm; + + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_ENTRY +{ + LPWSTR wzId; + LPWSTR wzSummary; + LPWSTR wzTitle; + FILETIME ftPublished; + FILETIME ftUpdated; + + ATOM_CONTENT* pContent; + + DWORD cAuthors; + ATOM_AUTHOR* rgAuthors; + + DWORD cCategories; + ATOM_CATEGORY* rgCategories; + + DWORD cLinks; + ATOM_LINK* rgLinks; + + IXMLDOMNode* pixn; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_FEED +{ + LPWSTR wzGenerator; + LPWSTR wzIcon; + LPWSTR wzId; + LPWSTR wzLogo; + LPWSTR wzSubtitle; + LPWSTR wzTitle; + FILETIME ftUpdated; + + DWORD cAuthors; + ATOM_AUTHOR* rgAuthors; + + DWORD cCategories; + ATOM_CATEGORY* rgCategories; + + DWORD cEntries; + ATOM_ENTRY* rgEntries; + + DWORD cLinks; + ATOM_LINK* rgLinks; + + IXMLDOMNode* pixn; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +HRESULT DAPI AtomInitialize( + ); + +void DAPI AtomUninitialize( + ); + +HRESULT DAPI AtomParseFromString( + __in_z LPCWSTR wzAtomString, + __out ATOM_FEED **ppFeed + ); + +HRESULT DAPI AtomParseFromFile( + __in_z LPCWSTR wzAtomFile, + __out ATOM_FEED **ppFeed + ); + +HRESULT DAPI AtomParseFromDocument( + __in IXMLDOMDocument* pixdDocument, + __out ATOM_FEED **ppFeed + ); + +void DAPI AtomFreeFeed( + __in_xcount(pFeed->cItems) ATOM_FEED* pFeed + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +// macro definitions + +#define ReleaseBuffer ReleaseMem +#define ReleaseNullBuffer ReleaseNullMem +#define BuffFree MemFree + + +// function declarations + +HRESULT BuffReadNumber( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD* pdw + ); +HRESULT BuffReadNumber64( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD64* pdw64 + ); +HRESULT BuffReadPointer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD_PTR* pdw +); +HRESULT BuffReadString( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPWSTR* pscz + ); +HRESULT BuffReadStringAnsi( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPSTR* pscz + ); +HRESULT BuffReadStream( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_inout_bcount(*pcbStream) BYTE** ppbStream, + __out SIZE_T* pcbStream + ); + +HRESULT BuffWriteNumber( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD dw + ); +HRESULT BuffWriteNumber64( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD64 dw64 + ); +HRESULT BuffWritePointer( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD_PTR dw +); +HRESULT BuffWriteString( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCWSTR scz + ); +HRESULT BuffWriteStringAnsi( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCSTR scz + ); +HRESULT BuffWriteStream( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_bcount(cbStream) const BYTE* pbStream, + __in SIZE_T cbStream + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +enum BUNDLE_INSTALL_CONTEXT +{ + BUNDLE_INSTALL_CONTEXT_MACHINE, + BUNDLE_INSTALL_CONTEXT_USER, +}; + + +/******************************************************************** +BundleGetBundleInfo - Queries the bundle installation metadata for a given property + +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) + The bundle is not installed + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) + The property is unrecognized + HRESULT_FROM_WIN32(ERROR_MORE_DATA) + A buffer is too small to hold the requested data. + E_NOTIMPL: + Tried to read a bundle attribute for a type which has not been implemented + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ +HRESULT DAPI BundleGetBundleInfo( + __in_z LPCWSTR szBundleId, // Bundle code + __in_z LPCWSTR szAttribute, // attribute name + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired + __inout_opt LPDWORD pcchValueBuf // in/out buffer character count + ); + +/******************************************************************** +BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code + +NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long. + The first 38 characters are for the GUID, and the last character is for the terminating null character. +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ +HRESULT DAPI BundleEnumRelatedBundle( + __in_z LPCWSTR lpUpgradeCode, + __in BUNDLE_INSTALL_CONTEXT context, + __inout PDWORD pdwStartIndex, + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + +// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method +// First argument is the name of splitting cabinet without extension e.g. "cab1" +// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" +// Third argument is the file token of the first file present in the splitting cabinet +typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR); + +#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866) + +#ifdef __cplusplus +extern "C" { +#endif + +extern const int CABC_HANDLE_BYTES; + +// time vs. space trade-off +typedef enum COMPRESSION_TYPE +{ + COMPRESSION_TYPE_NONE, // fastest + COMPRESSION_TYPE_LOW, + COMPRESSION_TYPE_MEDIUM, + COMPRESSION_TYPE_HIGH, // smallest + COMPRESSION_TYPE_MSZIP +} COMPRESSION_TYPE; + +// functions +HRESULT DAPI CabCBegin( + __in_z LPCWSTR wzCab, + __in_z LPCWSTR wzCabDir, + __in DWORD dwMaxFiles, + __in DWORD dwMaxSize, + __in DWORD dwMaxThresh, + __in COMPRESSION_TYPE ct, + __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext + ); +HRESULT DAPI CabCNextCab( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); +HRESULT DAPI CabCAddFile( + __in_z LPCWSTR wzFile, + __in_z_opt LPCWSTR wzToken, + __in_opt PMSIFILEHASHINFO pmfHash, + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); +HRESULT DAPI CabCFinish( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback + ); +void DAPI CabCCancel( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + + +// callback function prototypes +typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile); +typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); +typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); +typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod); +typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile); + +typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile); +typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile); +typedef HRESULT (*CAB_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); + +// function type with calling convention of __stdcall that .NET 1.1 understands only +// .NET 2.0 will not need this +typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin); + + +// functions +HRESULT DAPI CabInitialize( + __in BOOL fDelayLoad + ); +void DAPI CabUninitialize( + ); + +HRESULT DAPI CabExtract( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzExtractFile, + __in_z LPCWSTR wzExtractDir, + __in_opt CAB_CALLBACK_PROGRESS pfnProgress, + __in_opt LPVOID pvContext, + __in DWORD64 dw64EmbeddedOffset + ); + +HRESULT DAPI CabEnumerate( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzEnumerateFile, + __in STDCALL_PFNFDINOTIFY pfnNotify, + __in DWORD64 dw64EmbeddedOffset + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ReleaseCertStore(p) if (p) { ::CertCloseStore(p, 0); p = NULL; } +#define ReleaseCertContext(p) if (p) { ::CertFreeCertificateContext(p); p = NULL; } +#define ReleaseCertChain(p) if (p) { ::CertFreeCertificateChain(p); p = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI CertReadProperty( + __in PCCERT_CONTEXT pCertContext, + __in DWORD dwProperty, + __out_bcount(*pcbValue) LPVOID pvValue, + __out_opt DWORD* pcbValue + ); + +HRESULT DAPI CertGetAuthenticodeSigningTimestamp( + __in CMSG_SIGNER_INFO* pSignerInfo, + __out FILETIME* pft + ); + +HRESULT DAPI GetCryptProvFromCert( + __in_opt HWND hwnd, + __in PCCERT_CONTEXT pCert, + __out HCRYPTPROV *phCryptProv, + __out DWORD *pdwKeySpec, + __in BOOL *pfDidCryptAcquire, + __deref_opt_out LPWSTR *ppwszTmpContainer, + __deref_opt_out LPWSTR *ppwszProviderName, + __out DWORD *pdwProviderType + ); + +HRESULT DAPI FreeCryptProvFromCert( + __in BOOL fAcquired, + __in HCRYPTPROV hProv, + __in_opt LPWSTR pwszCapiProvider, + __in DWORD dwProviderType, + __in_opt LPWSTR pwszTmpContainer + ); + +HRESULT DAPI GetProvSecurityDesc( + __in HCRYPTPROV hProv, + __deref_out SECURITY_DESCRIPTOR** pSecurity + ); + +HRESULT DAPI SetProvSecurityDesc( + __in HCRYPTPROV hProv, + __in SECURITY_DESCRIPTOR* pSecurity + ); + +BOOL DAPI CertHasPrivateKey( + __in PCCERT_CONTEXT pCertContext, + __out_opt DWORD* pdwKeySpec + ); + +HRESULT DAPI CertInstallSingleCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzName + ); +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ConsoleExitOnFailureSource(d, x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#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; } } +#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; } +#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; } +#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; } + + +#define ConsoleExitOnFailure(x, c, f, ...) ConsoleExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) +#define ConsoleExitOnLastError(x, c, f, ...) ConsoleExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) +#define ConsoleExitOnNull(p, x, e, c, f, ...) ConsoleExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, c, f, __VA_ARGS__) +#define ConsoleExitOnNullWithLastError(p, x, c, f, ...) ConsoleExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, c, f, __VA_ARGS__) +#define ConsoleExitWithLastError(x, c, f, ...) ConsoleExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) + +// enums +typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR; + +// structs + +// functions +HRESULT DAPI ConsoleInitialize(); +void DAPI ConsoleUninitialize(); + +void DAPI ConsoleGreen(); +void DAPI ConsoleRed(); +void DAPI ConsoleYellow(); +void DAPI ConsoleNormal(); + +HRESULT DAPI ConsoleWrite( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI ConsoleWriteLine( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI ConsoleWriteError( + HRESULT hrError, + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI ConsoleReadW( + __deref_out_z LPWSTR* ppwzBuffer + ); + +HRESULT DAPI ConsoleReadStringA( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ); +HRESULT DAPI ConsoleReadStringW( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ); + +HRESULT DAPI ConsoleReadNonBlockingW( + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, + __out DWORD* pcchSize, + BOOL fReadLine + ); + +HRESULT DAPI ConsoleSetReadHidden(void); +HRESULT DAPI ConsoleSetReadNormal(void); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ReleaseCryptMsg(p) if (p) { ::CryptMsgClose(p); p = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + + +// Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE. +#define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE +#define MD5_HASH_LEN 16 +#define SHA1_HASH_LEN 20 +#define SHA256_HASH_LEN 32 +#define SHA512_HASH_LEN 64 + +typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)( + __inout PVOID Memory, + __in ULONG MemoryLength, + __in ULONG OptionFlags + ); + +typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)( + __inout PVOID Memory, + __in ULONG MemoryLength, + __in ULONG OptionFlags + ); + +typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +// function declarations + +HRESULT DAPI CrypInitialize(); +void DAPI CrypUninitialize(); + +HRESULT DAPI CrypDecodeObject( + __in_z LPCSTR szStructType, + __in_ecount(cbData) const BYTE* pbData, + __in DWORD cbData, + __in DWORD dwFlags, + __out LPVOID* ppvObject, + __out_opt DWORD* pcbObject + ); + +HRESULT DAPI CrypMsgGetParam( + __in HCRYPTMSG hCryptMsg, + __in DWORD dwType, + __in DWORD dwIndex, + __out LPVOID* ppvData, + __out_opt DWORD* pcbData + ); + +HRESULT DAPI CrypHashFile( + __in_z LPCWSTR wzFilePath, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ); + +HRESULT DAPI CrypHashFileHandle( + __in HANDLE hFile, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ); + +HRESULT DAPI CrypHashBuffer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash + ); + +HRESULT DAPI CrypEncryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +HRESULT DAPI CrypDecryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); } +#define ReleaseNullDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); rg = NULL; } + +typedef struct _DEPENDENCY +{ + LPWSTR sczKey; + LPWSTR sczName; +} DEPENDENCY; + + +/*************************************************************************** + DepGetProviderInformation - gets the various pieces of data registered + with a dependency. + + Note: Returns E_NOTFOUND if the dependency was not found. +***************************************************************************/ +DAPI_(HRESULT) DepGetProviderInformation( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __deref_out_z_opt LPWSTR* psczId, + __deref_out_z_opt LPWSTR* psczName, + __deref_out_z_opt LPWSTR* psczVersion + ); + +/*************************************************************************** + DepCheckDependency - Checks that the dependency is registered and within + the proper version range. + + Note: Returns E_NOTFOUND if the dependency was not found. +***************************************************************************/ +DAPI_(HRESULT) DepCheckDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes, + __in STRINGDICT_HANDLE sdDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ); + +/*************************************************************************** + DepCheckDependents - Checks if any dependents are still installed for the + given provider key. + +***************************************************************************/ +DAPI_(HRESULT) DepCheckDependents( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __reserved int iAttributes, + __in C_STRINGDICT_HANDLE sdIgnoredDependents, + __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, + __inout LPUINT pcDependents + ); + +/*************************************************************************** + DepRegisterDependency - Registers the dependency provider. + +***************************************************************************/ +DAPI_(HRESULT) DepRegisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z LPCWSTR wzVersion, + __in_z LPCWSTR wzDisplayName, + __in_z_opt LPCWSTR wzId, + __in int iAttributes + ); + +/*************************************************************************** + DepDependentExists - Determines if a dependent is registered. + + Note: Returns S_OK if dependent is registered. + Returns E_FILENOTFOUND if dependent is not registered +***************************************************************************/ +DAPI_(HRESULT) DepDependentExists( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DepRegisterDependent - Registers a dependent under the dependency provider. + +***************************************************************************/ +DAPI_(HRESULT) DepRegisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes + ); + +/*************************************************************************** + DepUnregisterDependency - Removes the dependency provider. + + Note: Caller should call CheckDependents prior to remove a dependency. + Returns E_FILENOTFOUND if the dependency is not registered. +***************************************************************************/ +DAPI_(HRESULT) DepUnregisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DepUnregisterDependent - Removes a dependent under the dependency provider. + + Note: Returns E_FILENOTFOUND if neither the dependency or dependent are + registered. + ***************************************************************************/ +DAPI_(HRESULT) DepUnregisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DependencyArrayAlloc - Allocates or expands an array of DEPENDENCY structs. + +***************************************************************************/ +DAPI_(HRESULT) DepDependencyArrayAlloc( + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __in_z LPCWSTR wzKey, + __in_z_opt LPCWSTR wzName + ); + +/*************************************************************************** + DepDependencyArrayFree - Frees an array of DEPENDENCY structs. + +***************************************************************************/ +DAPI_(void) DepDependencyArrayFree( + __in_ecount(cDependencies) DEPENDENCY* rgDependencies, + __in UINT cDependencies + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); } +#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; } + +typedef void* STRINGDICT_HANDLE; +typedef const void* C_STRINGDICT_HANDLE; + +extern const int STRINGDICT_HANDLE_BYTES; + +typedef enum DICT_FLAG +{ + DICT_FLAG_NONE = 0, + DICT_FLAG_CASEINSENSITIVE = 1 +} DICT_FLAG; + +HRESULT DAPI DictCreateWithEmbeddedKey( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in_opt void **ppvArray, + __in size_t cByteOffset, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCreateStringList( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCreateStringListFromArray( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCompareStringListToArray( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray + ); +HRESULT DAPI DictAddKey( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString + ); +HRESULT DAPI DictAddValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in void *pvValue + ); +HRESULT DAPI DictKeyExists( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString + ); +HRESULT DAPI DictGetValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString, + __out void **ppvValue + ); +void DAPI DictDestroy( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +typedef enum DIR_DELETE +{ + DIR_DELETE_FILES = 1, + DIR_DELETE_RECURSE = 2, + DIR_DELETE_SCHEDULE = 4, +} DIR_DELETE; + +#ifdef __cplusplus +extern "C" { +#endif + +BOOL DAPI DirExists( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); + +HRESULT DAPI DirCreateTempPath( + __in_z LPCWSTR wzPrefix, + __out_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD cchPath + ); + +HRESULT DAPI DirEnsureExists( + __in_z LPCWSTR wzPath, + __in_opt LPSECURITY_ATTRIBUTES psa + ); + +HRESULT DAPI DirEnsureDelete( + __in_z LPCWSTR wzPath, + __in BOOL fDeleteFiles, + __in BOOL fRecurse + ); + +HRESULT DAPI DirEnsureDeleteEx( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ); + +DWORD DAPI DirDeleteEmptyDirectoriesToRoot( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ); + +HRESULT DAPI DirGetCurrent( + __deref_out_z LPWSTR* psczCurrentDirectory + ); + +HRESULT DAPI DirSetCurrent( + __in_z LPCWSTR wzDirectory + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef HRESULT (WINAPI *LPAUTHENTICATION_ROUTINE)( + __in LPVOID pVoid, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); + +typedef int (WINAPI *LPCANCEL_ROUTINE)( + __in HRESULT hrError, + __in_z_opt LPCWSTR wzError, + __in BOOL fAllowRetry, + __in_opt LPVOID pvContext + ); + +// structs +typedef struct _DOWNLOAD_SOURCE +{ + LPWSTR sczUrl; + LPWSTR sczUser; + LPWSTR sczPassword; +} DOWNLOAD_SOURCE; + +typedef struct _DOWNLOAD_CACHE_CALLBACK +{ + LPPROGRESS_ROUTINE pfnProgress; + LPCANCEL_ROUTINE pfnCancel; + LPVOID pv; +} DOWNLOAD_CACHE_CALLBACK; + +typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK +{ + LPAUTHENTICATION_ROUTINE pfnAuthenticate; + LPVOID pv; +} DOWNLOAD_AUTHENTICATION_CALLBACK; + + +// functions + +HRESULT DAPI DownloadUrl( + __in DOWNLOAD_SOURCE* pDownloadSource, + __in DWORD64 dw64AuthoredDownloadSize, + __in LPCWSTR wzDestinationPath, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ); + + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// from WinUser.h +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif +#ifndef USER_DEFAULT_SCREEN_DPI +#define USER_DEFAULT_SCREEN_DPI 96 +#endif + +typedef enum DPIU_AWARENESS +{ + DPIU_AWARENESS_NONE = 0x0, + DPIU_AWARENESS_SYSTEM = 0x1, + DPIU_AWARENESS_PERMONITOR = 0x2, + DPIU_AWARENESS_PERMONITORV2 = 0x4, + DPIU_AWARENESS_GDISCALED = 0x8, +} DPIU_PROCESS_AWARENESS; + +typedef struct _DPIU_MONITOR_CONTEXT +{ + UINT nDpi; + MONITORINFOEXW mi; +} DPIU_MONITOR_CONTEXT; + +typedef struct _DPIU_WINDOW_CONTEXT +{ + UINT nDpi; +} DPIU_WINDOW_CONTEXT; + +typedef BOOL (APIENTRY* PFN_ADJUSTWINDOWRECTEXFORDPI)( + __in LPRECT lpRect, + __in DWORD dwStyle, + __in BOOL bMenu, + __in DWORD dwExStyle, + __in UINT dpi + ); +typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( + __in HWND hwnd + ); +typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARE)(); +typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARENESSCONTEXT)( + __in DPI_AWARENESS_CONTEXT value + ); + +#ifdef DPI_ENUMS_DECLARED +typedef HRESULT(APIENTRY* PFN_GETDPIFORMONITOR)( + __in HMONITOR hmonitor, + __in MONITOR_DPI_TYPE dpiType, + __in UINT* dpiX, + __in UINT* dpiY + ); +typedef HRESULT(APIENTRY* PFN_SETPROCESSDPIAWARENESS)( + __in PROCESS_DPI_AWARENESS value + ); +#endif + +void DAPI DpiuInitialize(); +void DAPI DpiuUninitialize(); + +/******************************************************************** + DpiuAdjustWindowRect - calculate the required size of the window rectangle, + based on the desired size of the client rectangle + and the provided DPI. + +*******************************************************************/ +void DAPI DpiuAdjustWindowRect( + __in RECT* pWindowRect, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle, + __in UINT nDpi + ); + +/******************************************************************** + DpiuGetMonitorContextFromPoint - get the DPI context of the monitor from the given point. + +*******************************************************************/ +HRESULT DAPI DpiuGetMonitorContextFromPoint( + __in const POINT* pt, + __out DPIU_MONITOR_CONTEXT** ppMonitorContext + ); + +/******************************************************************** + DpiuGetWindowContext - get the DPI context of the given window. + +*******************************************************************/ +void DAPI DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ); + +/******************************************************************** + DpiuScaleValue - scale the value to the target DPI. + +*******************************************************************/ +int DAPI DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ); + +/******************************************************************** + DpiuSetProcessDpiAwareness - set the process DPI awareness. The ranking is + PERMONITORV2 > PERMONITOR > SYSTEM > GDISCALED > NONE. + +*******************************************************************/ +HRESULT DAPI DpiuSetProcessDpiAwareness( + __in DPIU_AWARENESS supportedAwareness, + __in_opt DPIU_AWARENESS* pSelectedAwareness + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "dutilsources.h" + +#define DAPI __stdcall +#define DAPIV __cdecl // used only for functions taking variable length arguments + +#define DAPI_(type) EXTERN_C type DAPI +#define DAPIV_(type) EXTERN_C type DAPIV + + +// asserts and traces +typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); + +typedef void (CALLBACK *DUTIL_CALLBACK_TRACEERROR)( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************** + DutilInitialize - initialize dutil. + +*******************************************************************/ +HRESULT DAPI DutilInitialize( + __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback + ); + +/******************************************************************** + DutilUninitialize - uninitialize dutil. + +*******************************************************************/ +void DAPI DutilUninitialize(); + +void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule); +void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn); +void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine); +void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z __format_string LPCSTR szMessage); + +void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames); +REPORT_LEVEL DAPI Dutil_TraceGetLevel(); +void DAPIV Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...); +void DAPIV Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); +void 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, ...); +void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError); + +#ifdef __cplusplus +} +#endif + + +#ifdef DEBUG + +#define AssertSetModule(m) (void)Dutil_SetAssertModule(m) +#define AssertSetDisplayFunction(pfn) (void)Dutil_SetAssertDisplayFunction(pfn) +#define Assert(f) ((f) ? (void)0 : (void)Dutil_Assert(__FILE__, __LINE__)) +#define AssertSz(f, sz) ((f) ? (void)0 : (void)Dutil_AssertSz(__FILE__, __LINE__, sz)) + +#define TraceSetLevel(l, f) (void)Dutil_TraceSetLevel(l, f) +#define TraceGetLevel() (REPORT_LEVEL)Dutil_TraceGetLevel() +#define Trace(l, f, ...) (void)Dutil_Trace(__FILE__, __LINE__, l, f, __VA_ARGS__) +#define TraceError(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_ERROR, x, f, __VA_ARGS__) +#define TraceErrorDebug(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_DEBUG, x, f, __VA_ARGS__) + +#else // !DEBUG + +#define AssertSetModule(m) +#define AssertSetDisplayFunction(pfn) +#define Assert(f) +#define AssertSz(f, sz) + +#define TraceSetLevel(l, f) +#define Trace(l, f, ...) +#define TraceError(x, f, ...) +#define TraceErrorDebug(x, f, ...) + +#endif // DEBUG + +// DUTIL_SOURCE_DEFAULT can be overriden +#ifndef DUTIL_SOURCE_DEFAULT +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_UNKNOWN +#endif + +// Exit macros +#define ExitFunction() { goto LExit; } +#define ExitFunction1(x) { x; goto LExit; } + +#define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; } + +#define ExitTraceSource(d, x, s, ...) { TraceError(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_ERROR, d, x, s, __VA_ARGS__); } +#define ExitTraceDebugSource(d, x, s, ...) { TraceErrorDebug(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, d, x, s, __VA_ARGS__); } + +#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; } } +#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; } } +#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; } +#define ExitOnFailureSource(d, x, s, ...) if (FAILED(x)) { ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnRootFailureSource(d, x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnFailureDebugTraceSource(d, x, s, ...) if (FAILED(x)) { ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } +#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; } +#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; } +#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; } +#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; } +#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; } + +#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) +#define ExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) +#define ExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) +#define ExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) +#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__) + +// release macros +#define ReleaseObject(x) if (x) { x->Release(); } +#define ReleaseObjectArray(prg, cel) if (prg) { for (DWORD Dutil_ReleaseObjectArrayIndex = 0; Dutil_ReleaseObjectArrayIndex < cel; ++Dutil_ReleaseObjectArrayIndex) { ReleaseObject(prg[Dutil_ReleaseObjectArrayIndex]); } ReleaseMem(prg); } +#define ReleaseVariant(x) { ::VariantClear(&x); } +#define ReleaseNullObject(x) if (x) { (x)->Release(); x = NULL; } +#define ReleaseCertificate(x) if (x) { ::CertFreeCertificateContext(x); x=NULL; } +#define ReleaseHandle(x) if (x) { ::CloseHandle(x); x = NULL; } + + +// useful defines and macros +#define Unused(x) ((void)x) + +#ifndef countof +#if 1 +#define countof(ary) (sizeof(ary) / sizeof(ary[0])) +#else +#ifndef __cplusplus +#define countof(ary) (sizeof(ary) / sizeof(ary[0])) +#else +template static char countofVerify(void const *, T) throw() { return 0; } +template static void countofVerify(T *const, T *const *) throw() {}; +#define countof(arr) (sizeof(countofVerify(arr,&(arr))) * sizeof(arr)/sizeof(*(arr))) +#endif +#endif +#endif + +#define roundup(x, n) roundup_typed(x, n, DWORD) +#define roundup_typed(x, n, t) (((t)(x) + ((t)(n) - 1)) & ~((t)(n) - 1)) + +#define HRESULT_FROM_RPC(x) ((HRESULT) ((x) | FACILITY_RPC)) + +#ifndef MAXSIZE_T +#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0)) +#endif + +typedef const BYTE* LPCBYTE; + +#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) +#define E_PATHNOTFOUND HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) +#define E_INVALIDDATA HRESULT_FROM_WIN32(ERROR_INVALID_DATA) +#define E_INVALIDSTATE HRESULT_FROM_WIN32(ERROR_INVALID_STATE) +#define E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) +#define E_MOREDATA HRESULT_FROM_WIN32(ERROR_MORE_DATA) +#define E_NOMOREITEMS HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) +#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) +#define E_MODNOTFOUND HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) +#define E_BADCONFIGURATION HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION) + +#define AddRefAndRelease(x) { x->AddRef(); x->Release(); } + +#define MAKEDWORD(lo, hi) ((DWORD)MAKELONG(lo, hi)) +#define MAKEQWORDVERSION(mj, mi, b, r) (((DWORD64)MAKELONG(r, b)) | (((DWORD64)MAKELONG(mi, mj)) << 32)) + + +#ifdef __cplusplus +extern "C" { +#endif + +// other functions +HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule); +HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +typedef enum DUTIL_SOURCE +{ + DUTIL_SOURCE_UNKNOWN, + DUTIL_SOURCE_ACLUTIL, + DUTIL_SOURCE_APPUTIL, + DUTIL_SOURCE_APUPUTIL, + DUTIL_SOURCE_ATOMUTIL, + DUTIL_SOURCE_BUFFUTIL, + DUTIL_SOURCE_BUTIL, + DUTIL_SOURCE_CABCUTIL, + DUTIL_SOURCE_CABUTIL, + DUTIL_SOURCE_CERTUTIL, + DUTIL_SOURCE_CONUTIL, + DUTIL_SOURCE_CRYPUTIL, + DUTIL_SOURCE_DEPUTIL, + DUTIL_SOURCE_DICTUTIL, + DUTIL_SOURCE_DIRUTIL, + DUTIL_SOURCE_DLUTIL, + DUTIL_SOURCE_DPIUTIL, + DUTIL_SOURCE_DUTIL, + DUTIL_SOURCE_ESEUTIL, + DUTIL_SOURCE_FILEUTIL, + DUTIL_SOURCE_GDIPUTIL, + DUTIL_SOURCE_GUIDUTIL, + DUTIL_SOURCE_IIS7UTIL, + DUTIL_SOURCE_INETUTIL, + DUTIL_SOURCE_INIUTIL, + DUTIL_SOURCE_JSONUTIL, + DUTIL_SOURCE_LOCUTIL, + DUTIL_SOURCE_LOGUTIL, + DUTIL_SOURCE_MEMUTIL, + DUTIL_SOURCE_METAUTIL, + DUTIL_SOURCE_MONUTIL, + DUTIL_SOURCE_OSUTIL, + DUTIL_SOURCE_PATHUTIL, + DUTIL_SOURCE_PERFUTIL, + DUTIL_SOURCE_POLCUTIL, + DUTIL_SOURCE_PROCUTIL, + DUTIL_SOURCE_REGUTIL, + DUTIL_SOURCE_RESRUTIL, + DUTIL_SOURCE_RESWUTIL, + DUTIL_SOURCE_REXUTIL, + DUTIL_SOURCE_RMUTIL, + DUTIL_SOURCE_RSSUTIL, + DUTIL_SOURCE_SCEUTIL, + DUTIL_SOURCE_SCZUTIL, + DUTIL_SOURCE_SHELUTIL, + DUTIL_SOURCE_SQLUTIL, + DUTIL_SOURCE_SRPUTIL, + DUTIL_SOURCE_STRUTIL, + DUTIL_SOURCE_SVCUTIL, + DUTIL_SOURCE_THMUTIL, + DUTIL_SOURCE_TIMEUTIL, + DUTIL_SOURCE_UNCUTIL, + DUTIL_SOURCE_URIUTIL, + DUTIL_SOURCE_USERUTIL, + DUTIL_SOURCE_WIUTIL, + DUTIL_SOURCE_WUAUTIL, + DUTIL_SOURCE_XMLUTIL, + DUTIL_SOURCE_VERUTIL, + + DUTIL_SOURCE_EXTERNAL = 256, +} DUTIL_SOURCE; + +typedef enum REPORT_LEVEL +{ + REPORT_NONE, // turns off report (only valid for XXXSetLevel()) + REPORT_WARNING, // written if want only warnings or reporting is on in general + REPORT_STANDARD, // written if reporting is on + REPORT_VERBOSE, // written only if verbose reporting is on + REPORT_DEBUG, // reporting useful when debugging code + REPORT_ERROR, // always gets reported, but can never be specified +} 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); } +#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; } + +struct ESE_COLUMN_SCHEMA +{ + JET_COLUMNID jcColumn; + LPCWSTR pszName; + JET_COLTYP jcColumnType; + BOOL fKey; // If this column is part of the key of the table + BOOL fFixed; + BOOL fNullable; + BOOL fAutoIncrement; +}; + +struct ESE_TABLE_SCHEMA +{ + JET_TABLEID jtTable; + LPCWSTR pszName; + DWORD dwColumns; + ESE_COLUMN_SCHEMA *pcsColumns; +}; + +struct ESE_DATABASE_SCHEMA +{ + DWORD dwTables; + ESE_TABLE_SCHEMA *ptsTables; +}; + +typedef enum ESE_QUERY_TYPE +{ + ESE_QUERY_EXACT, + ESE_QUERY_FROM_TOP, + ESE_QUERY_FROM_BOTTOM +} ESE_QUERY_TYPE; + +typedef void* ESE_QUERY_HANDLE; + +HRESULT DAPI EseBeginSession( + __out JET_INSTANCE *pjiInstance, + __out JET_SESID *pjsSession, + __in_z LPCWSTR pszInstance, + __in_z LPCWSTR pszPath + ); +HRESULT DAPI EseEndSession( + __in JET_INSTANCE jiInstance, + __in JET_SESID jsSession + ); +HRESULT DAPI EseEnsureDatabase( + __in JET_SESID jsSession, + __in_z LPCWSTR pszFile, + __in ESE_DATABASE_SCHEMA *pdsSchema, + __out JET_DBID* pjdbDb, + __in BOOL fExclusive, + __in BOOL fReadonly + ); +HRESULT DAPI EseCloseDatabase( + __in JET_SESID jsSession, + __in JET_DBID jdbDb + ); +HRESULT DAPI EseCreateTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ); +HRESULT DAPI EseOpenTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ); +HRESULT DAPI EseCloseTable( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ); +HRESULT DAPI EseEnsureColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __in JET_COLTYP jcColumnType, + __in ULONG ulColumnSize, + __in BOOL fFixed, + __in BOOL fNullable, + __out_opt JET_COLUMNID *pjcColumn + ); +HRESULT DAPI EseGetColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __out JET_COLUMNID *pjcColumn + ); +HRESULT DAPI EseMoveCursor( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in LONG lRow + ); +HRESULT DAPI EseDeleteRow( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ); +HRESULT DAPI EseBeginTransaction( + __in JET_SESID jsSession + ); +HRESULT DAPI EseRollbackTransaction( + __in JET_SESID jsSession, + __in BOOL fAll + ); +HRESULT DAPI EseCommitTransaction( + __in JET_SESID jsSession + ); +HRESULT DAPI EsePrepareUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ULONG ulPrep + ); +HRESULT DAPI EseFinishUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in BOOL fSeekToInsertedRecord + ); +HRESULT DAPI EseSetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI EseSetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in DWORD dwValue + ); +HRESULT DAPI EseSetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in BOOL fValue + ); +HRESULT DAPI EseSetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_z LPCWSTR pszValue + ); +HRESULT DAPI EseSetColumnEmpty( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn + ); +HRESULT DAPI EseGetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT DAPI EseGetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out DWORD *pdwValue + ); +HRESULT DAPI EseGetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out BOOL *pfValue + ); +HRESULT DAPI EseGetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out LPWSTR *ppszValue + ); + +// Call this once for each key column in the table +HRESULT DAPI EseBeginQuery( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ESE_QUERY_TYPE qtQueryType, + __out ESE_QUERY_HANDLE *peqhHandle + ); +HRESULT DAPI EseSetQueryColumnBinary( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnDword( + __in ESE_QUERY_HANDLE eqhHandle, + __in DWORD dwData, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnBool( + __in ESE_QUERY_HANDLE eqhHandle, + __in BOOL fValue, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnString( + __in ESE_QUERY_HANDLE eqhHandle, + __in_z LPCWSTR pszString, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseFinishQuery( + __in ESE_QUERY_HANDLE eqhHandle + ); +// Once all columns have been set up, call this and read the result +HRESULT DAPI EseRunQuery( + __in ESE_QUERY_HANDLE eqhHandle + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseFile(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } +#define ReleaseFileHandle(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } +#define ReleaseFileFindHandle(h) if (INVALID_HANDLE_VALUE != h) { ::FindClose(h); h = INVALID_HANDLE_VALUE; } + +#define FILEMAKEVERSION(major, minor, build, revision) static_cast((static_cast(major & 0xFFFF) << 48) \ + | (static_cast(minor & 0xFFFF) << 32) \ + | (static_cast(build & 0xFFFF) << 16) \ + | (static_cast(revision & 0xFFFF))) + +typedef enum FILE_ARCHITECTURE +{ + FILE_ARCHITECTURE_UNKNOWN, + FILE_ARCHITECTURE_X86, + FILE_ARCHITECTURE_X64, + FILE_ARCHITECTURE_IA64, +} FILE_ARCHITECTURE; + +typedef enum FILE_ENCODING +{ + FILE_ENCODING_UNSPECIFIED = 0, + // TODO: distinguish between non-BOM utf-8 and ANSI in the future? + FILE_ENCODING_UTF8, + FILE_ENCODING_UTF8_WITH_BOM, + FILE_ENCODING_UTF16, + FILE_ENCODING_UTF16_WITH_BOM, +} FILE_ENCODING; + + +LPWSTR DAPI FileFromPath( + __in_z LPCWSTR wzPath + ); +HRESULT DAPI FileResolvePath( + __in_z LPCWSTR wzRelativePath, + __out LPWSTR *ppwzFullPath + ); +HRESULT DAPI FileStripExtension( + __in_z LPCWSTR wzFileName, + __out LPWSTR *ppwzFileNameNoExtension + ); +HRESULT DAPI FileChangeExtension( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzNewExtension, + __out LPWSTR *ppwzFileNameNewExtension + ); +HRESULT DAPI FileAddSuffixToBaseName( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzSuffix, + __out_z LPWSTR* psczNewFileName + ); +HRESULT DAPI FileVersionFromString( + __in_z LPCWSTR wzVersion, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ); +HRESULT DAPI FileVersionFromStringEx( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __out DWORD64* pqwVersion + ); +HRESULT DAPI FileVersionToStringEx( + __in DWORD64 qwVersion, + __out LPWSTR* psczVersion + ); +HRESULT DAPI FileSetPointer( + __in HANDLE hFile, + __in DWORD64 dw64Move, + __out_opt DWORD64* pdw64NewPosition, + __in DWORD dwMoveMethod + ); +HRESULT DAPI FileSize( + __in_z LPCWSTR pwzFileName, + __out LONGLONG* pllSize + ); +HRESULT DAPI FileSizeByHandle( + __in HANDLE hFile, + __out LONGLONG* pllSize + ); +BOOL DAPI FileExistsEx( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); +BOOL DAPI FileExistsAfterRestart( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); +HRESULT DAPI FileRemoveFromPendingRename( + __in_z LPCWSTR wzPath + ); +HRESULT DAPI FileRead( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath + ); +HRESULT DAPI FileReadEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD dwShareMode + ); +HRESULT DAPI FileReadUntil( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD cbMaxRead + ); +HRESULT DAPI FileReadPartial( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK + ); +HRESULT DAPI FileReadPartialEx( + __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK, + __in DWORD dwShareMode + ); +HRESULT DAPI FileReadHandle( + __in HANDLE hFile, + __in_bcount(cbDest) LPBYTE pbDest, + __in SIZE_T cbDest + ); +HRESULT DAPI FileWrite( + __in_z LPCWSTR pwzFileName, + __in DWORD dwFlagsAndAttributes, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData, + __out_opt HANDLE* pHandle + ); +HRESULT DAPI FileWriteHandle( + __in HANDLE hFile, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData + ); +HRESULT DAPI FileCopyUsingHandles( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __out_opt DWORD64* pcbCopied + ); +HRESULT DAPI FileCopyUsingHandlesWithProgress( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + __in_opt LPVOID lpData + ); +HRESULT DAPI FileEnsureCopy( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite + ); +HRESULT DAPI FileEnsureCopyWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); +HRESULT DAPI FileEnsureMove( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy + ); +HRESULT DAPI FileEnsureMoveWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); +HRESULT DAPI FileCreateTemp( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ); +HRESULT DAPI FileCreateTempW( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ); +HRESULT DAPI FileVersion( + __in_z LPCWSTR wzFilename, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ); +HRESULT DAPI FileIsSame( + __in_z LPCWSTR wzFile1, + __in_z LPCWSTR wzFile2, + __out LPBOOL lpfSameFile + ); +HRESULT DAPI FileEnsureDelete( + __in_z LPCWSTR wzFile + ); +HRESULT DAPI FileGetTime( + __in_z LPCWSTR wzFile, + __out_opt LPFILETIME lpCreationTime, + __out_opt LPFILETIME lpLastAccessTime, + __out_opt LPFILETIME lpLastWriteTime + ); +HRESULT DAPI FileSetTime( + __in_z LPCWSTR wzFile, + __in_opt const FILETIME *lpCreationTime, + __in_opt const FILETIME *lpLastAccessTime, + __in_opt const FILETIME *lpLastWriteTime + ); +HRESULT DAPI FileResetTime( + __in_z LPCWSTR wzFile + ); +HRESULT DAPI FileExecutableArchitecture( + __in_z LPCWSTR wzFile, + __out FILE_ARCHITECTURE *pArchitecture + ); +HRESULT DAPI FileToString( + __in_z LPCWSTR wzFile, + __out LPWSTR *psczString, + __out_opt FILE_ENCODING *pfeEncoding + ); +HRESULT DAPI FileFromString( + __in_z LPCWSTR wzFile, + __in DWORD dwFlagsAndAttributes, + __in_z LPCWSTR sczString, + __in FILE_ENCODING feEncoding + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#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; } } +#define ExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEFAULT, g, x, s, __VA_ARGS__) + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI GdipInitialize( + __in const Gdiplus::GdiplusStartupInput* pInput, + __out ULONG_PTR* pToken, + __out_opt Gdiplus::GdiplusStartupOutput *pOutput + ); + +void DAPI GdipUninitialize( + __in ULONG_PTR token + ); + +HRESULT DAPI GdipBitmapFromResource( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szId, + __out Gdiplus::Bitmap **ppBitmap + ); + +HRESULT DAPI GdipBitmapFromFile( + __in_z LPCWSTR wzFileName, + __out Gdiplus::Bitmap **ppBitmap + ); + +HRESULT DAPI GdipHresultFromStatus( + __in Gdiplus::Status gs + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUID_STRING_LENGTH 39 + +HRESULT DAPI GuidFixedCreate( + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid + ); + +HRESULT DAPI GuidCreate( + __deref_out_z LPWSTR* psczGuid + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// IIS Config schema names +#define IIS_CONFIG_ADD L"add" +#define IIS_CONFIG_ALLOWED L"allowed" +#define IIS_CONFIG_APPHOST_ROOT L"MACHINE/WEBROOT/APPHOST" +#define IIS_CONFIG_APPLICATION L"application" +#define IIS_CONFIG_APPPOOL L"applicationPool" +#define IIS_CONFIG_APPPOOL_AUTO L"autoStart" +#define IIS_CONFIG_APPPOOL_SECTION L"system.applicationHost/applicationPools" +#define IIS_CONFIG_AUTOSTART L"serverAutoStart" +#define IIS_CONFIG_BINDING L"binding" +#define IIS_CONFIG_BINDINGINFO L"bindingInformation" +#define IIS_CONFIG_BINDINGS L"bindings" +#define IIS_CONFIG_DESC L"description" +#define IIS_CONFIG_EXECUTABLE L"scriptProcessor" +#define IIS_CONFIG_ENABLED L"enabled" +#define IIS_CONFIG_ENABLE32 L"enable32BitAppOnWin64" +#define IIS_CONFIG_FILEEXT L"fileExtension" +#define IIS_CONFIG_FILTER L"filter" +#define IIS_CONFIG_GROUPID L"groupId" +#define IIS_CONFIG_HEADERS L"customHeaders" +#define IIS_CONFIG_HTTPERRORS_SECTION L"system.webServer/httpErrors" +#define IIS_CONFIG_ID L"id" +#define IIS_CONFIG_ISAPI_SECTION L"system.webServer/isapiFilters" +#define IIS_CONFIG_HTTPPROTO_SECTION L"system.webServer/httpProtocol" +#define IIS_CONFIG_LOG_SECTION L"system.applicationHost/log" +#define IIS_CONFIG_LOG_UTF8 L"logInUTF8" +#define IIS_CONFIG_LIMITS L"limits" +#define IIS_CONFIG_PIPELINEMODE L"managedPipelineMode" +#define IIS_CONFIG_MANAGEDRUNTIMEVERSION L"managedRuntimeVersion" +#define IIS_CONFIG_WEBLOG L"logFile" +#define IIS_CONFIG_LOGFORMAT L"logFormat" +#define IIS_CONFIG_MIMEMAP L"mimeMap" +#define IIS_CONFIG_MIMETYPE L"mimeType" +#define IIS_CONFIG_MODULES L"modules" +#define IIS_CONFIG_NAME L"name" +#define IIS_CONFIG_PATH L"path" +#define IIS_CONFIG_PHYSPATH L"physicalPath" +#define IIS_CONFIG_PROTOCOL L"protocol" +#define IIS_CONFIG_RESTRICTION_SECTION L"system.webServer/security/isapiCgiRestriction" +#define IIS_CONFIG_SITE L"site" +#define IIS_CONFIG_SITE_ID L"id" +#define IIS_CONFIG_SITES_SECTION L"system.applicationHost/sites" +#define IIS_CONFIG_CONNECTTIMEOUT L"connectionTimeout" +#define IIS_CONFIG_VDIR L"virtualDirectory" +#define IIS_CONFIG_VALUE L"value" +#define IIS_CONFIG_VERBS L"verb" +#define IIS_CONFIG_WEBLIMITS_SECTION L"system.applicationHost/webLimits" +#define IIS_CONFIG_WEBLIMITS_MAXBAND L"maxGlobalBandwidth" +#define IIS_CONFIG_TRUE L"true" +#define IIS_CONFIG_FALSE L"false" +#define IIS_CONFIG_ERROR L"error" +#define IIS_CONFIG_STATUSCODE L"statusCode" +#define IIS_CONFIG_SUBSTATUS L"subStatusCode" +#define IIS_CONFIG_LANGPATH L"prefixLanguageFilePath" +#define IIS_CONFIG_RESPMODE L"responseMode" +#define IIS_CONFIG_CLEAR L"clear" +#define IIS_CONFIG_RECYCLING L"recycling" +#define IIS_CONFIG_PEROIDRESTART L"periodicRestart" +#define IIS_CONFIG_TIME L"time" +#define IIS_CONFIG_REQUESTS L"requests" +#define IIS_CONFIG_SCHEDULE L"schedule" +#define IIS_CONFIG_MEMORY L"memory" +#define IIS_CONFIG_PRIVMEMORY L"privateMemory" +#define IIS_CONFIG_PROCESSMODEL L"processModel" +#define IIS_CONFIG_IDLETIMEOUT L"idleTimeout" +#define IIS_CONFIG_QUEUELENGTH L"queueLength" +#define IIS_CONFIG_IDENITITYTYPE L"identityType" +#define IIS_CONFIG_LOCALSYSTEM L"LocalSystem" +#define IIS_CONFIG_LOCALSERVICE L"LocalService" +#define IIS_CONFIG_NETWORKSERVICE L"NetworkService" +#define IIS_CONFIG_SPECIFICUSER L"SpecificUser" +#define IIS_CONFIG_APPLICATIONPOOLIDENTITY L"ApplicationPoolIdentity" +#define IIS_CONFIG_USERNAME L"userName" +#define IIS_CONFIG_PASSWORD L"password" +#define IIS_CONFIG_CPU L"cpu" +#define IIS_CONFIG_LIMIT L"limit" +#define IIS_CONFIG_CPU_ACTION L"action" +#define IIS_CONFIG_KILLW3WP L"KillW3wp" +#define IIS_CONFIG_NOACTION L"NoAction" +#define IIS_CONFIG_RESETINTERVAL L"resetInterval" +#define IIS_CONFIG_MAXWRKPROCESSES L"maxProcesses" +#define IIS_CONFIG_HANDLERS_SECTION L"system.webServer/handlers" +#define IIS_CONFIG_DEFAULTDOC_SECTION L"system.webServer/defaultDocument" +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" +#define IIS_CONFIG_SCRIPTERROR L"scriptErrorSentToBrowser" +#define IIS_CONFIG_STATICCONTENT_SECTION L"system.webServer/staticContent" +#define IIS_CONFIG_HTTPEXPIRES L"httpExpires" +#define IIS_CONFIG_MAXAGE L"cacheControlMaxAge" +#define IIS_CONFIG_CLIENTCACHE L"clientCache" +#define IIS_CONFIG_CACHECONTROLMODE L"cacheControlMode" +#define IIS_CONFIG_USEMAXAGE L"UseMaxAge" +#define IIS_CONFIG_USEEXPIRES L"UseExpires" +#define IIS_CONFIG_CACHECUST L"cacheControlCustom" +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" +#define IIS_CONFIG_SESSION L"session" +#define IIS_CONFIG_ALLOWSTATE L"allowSessionState" +#define IIS_CONFIG_TIMEOUT L"timeout" +#define IIS_CONFIG_BUFFERING L"bufferingOn" +#define IIS_CONFIG_PARENTPATHS L"enableParentPaths" +#define IIS_CONFIG_SCRIPTLANG L"scriptLanguage" +#define IIS_CONFIG_SCRIPTTIMEOUT L"scriptTimeout" +#define IIS_CONFIG_LIMITS L"limits" +#define IIS_CONFIG_ALLOWDEBUG L"appAllowDebugging" +#define IIS_CONFIG_ALLOWCLIENTDEBUG L"appAllowClientDebug" +#define IIS_CONFIG_CERTIFICATEHASH L"certificateHash" +#define IIS_CONFIG_CERTIFICATESTORENAME L"certificateStoreName" +#define IIS_CONFIG_HTTPLOGGING_SECTION L"system.webServer/httpLogging" +#define IIS_CONFIG_DONTLOG L"dontLog" + +typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID); +typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*); + +HRESULT DAPI Iis7PutPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT vtPut + ); + +HRESULT DAPI Iis7PutPropertyInteger( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in DWORD dValue + ); + +HRESULT DAPI Iis7PutPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPCWSTR wzString + ); + +HRESULT DAPI Iis7PutPropertyBool( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in BOOL fValue); + +HRESULT DAPI Iis7GetPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT* vtGet + ); + +HRESULT DAPI Iis7GetPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPWSTR* psczGet + ); + +struct IIS7_APPHOSTELEMENTCOMPARISON +{ + LPCWSTR sczElementName; + LPCWSTR sczAttributeName; + VARIANT* pvAttributeValue; + VARIANTCOMPARATORPROC pComparator; +}; + +BOOL DAPI Iis7IsMatchingAppHostElement( + __in IAppHostElement *pElement, + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison + ); + +HRESULT DAPI Iis7FindAppHostElementString( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementPath( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementInteger( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in DWORD dwAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementVariant( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in VARIANT* pvAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7EnumAppHostElements( + __in IAppHostElementCollection *pCollection, + __in ENUMAPHOSTELEMENTPROC pCallback, + __in LPVOID pContext, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostMethod( + __in IAppHostMethodCollection *pCollection, + __in LPCWSTR wzMethodName, + __out IAppHostMethod** ppMethod, + __out DWORD* pdwIndex + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } +#define ReleaseNullInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } + + +// functions +HRESULT DAPI InternetGetSizeByHandle( + __in HINTERNET hiFile, + __out LONGLONG* pllSize + ); + +HRESULT DAPI InternetGetCreateTimeByHandle( + __in HINTERNET hiFile, + __out LPFILETIME pft + ); + +HRESULT DAPI InternetQueryInfoString( + __in HINTERNET h, + __in DWORD dwInfo, + __deref_out_z LPWSTR* psczValue + ); + +HRESULT DAPI InternetQueryInfoNumber( + __in HINTERNET h, + __in DWORD dwInfo, + __inout LONG* plInfo + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); } +#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; } + +typedef void* INI_HANDLE; +typedef const void* C_INI_HANDLE; + +extern const int INI_HANDLE_BYTES; + +struct INI_VALUE +{ + LPCWSTR wzName; + LPCWSTR wzValue; + + DWORD dwLineNumber; +}; + +HRESULT DAPI IniInitialize( + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle + ); +void DAPI IniUninitialize( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle + ); +HRESULT DAPI IniSetOpenTag( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzOpenTagPrefix, + __in_z_opt LPCWSTR wzOpenTagPostfix + ); +HRESULT DAPI IniSetValueStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzValuePrefix, + __in_z_opt LPCWSTR wzValueSeparator + ); +HRESULT DAPI IniSetValueSeparatorException( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z LPCWSTR wzValueNamePrefix + ); +HRESULT DAPI IniSetCommentStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzLinePrefix + ); +HRESULT DAPI IniParse( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzPath, + __out_opt FILE_ENCODING *pfeEncodingFound + ); +// Gets the full value array, this includes values that may have been deleted +// (their value will be NULL) +HRESULT DAPI IniGetValueList( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, + __out DWORD *pcValues + ); +HRESULT DAPI IniGetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __deref_out_z LPWSTR* psczValue + ); +HRESULT DAPI IniSetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI IniWriteFile( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzPath, + __in FILE_ENCODING feOverrideEncoding + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum JSON_TOKEN +{ + JSON_TOKEN_NONE, + JSON_TOKEN_ARRAY_START, + JSON_TOKEN_ARRAY_VALUE, + JSON_TOKEN_ARRAY_END, + JSON_TOKEN_OBJECT_START, + JSON_TOKEN_OBJECT_KEY, + JSON_TOKEN_OBJECT_VALUE, + JSON_TOKEN_OBJECT_END, + JSON_TOKEN_VALUE, +} JSON_TOKEN; + +typedef struct _JSON_VALUE +{ +} JSON_VALUE; + +typedef struct _JSON_READER +{ + CRITICAL_SECTION cs; + LPWSTR sczJson; + + LPWSTR pwz; + JSON_TOKEN token; +} JSON_READER; + +typedef struct _JSON_WRITER +{ + CRITICAL_SECTION cs; + LPWSTR sczJson; + + JSON_TOKEN* rgTokenStack; + DWORD cTokens; + DWORD cMaxTokens; +} JSON_WRITER; + + +DAPI_(HRESULT) JsonInitializeReader( + __in_z LPCWSTR wzJson, + __in JSON_READER* pReader + ); + +DAPI_(void) JsonUninitializeReader( + __in JSON_READER* pReader + ); + +DAPI_(HRESULT) JsonReadNext( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken, + __out JSON_VALUE* pValue + ); + +DAPI_(HRESULT) JsonReadValue( + __in JSON_READER* pReader, + __in JSON_VALUE* pValue + ); + +DAPI_(HRESULT) JsonInitializeWriter( + __in JSON_WRITER* pWriter + ); + +DAPI_(void) JsonUninitializeWriter( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteBool( + __in JSON_WRITER* pWriter, + __in BOOL fValue + ); + +DAPI_(HRESULT) JsonWriteNumber( + __in JSON_WRITER* pWriter, + __in DWORD dwValue + ); + +DAPI_(HRESULT) JsonWriteString( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzValue + ); + +DAPI_(HRESULT) JsonWriteArrayStart( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteArrayEnd( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteObjectStart( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteObjectKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ); + +DAPI_(HRESULT) JsonWriteObjectEnd( + __in JSON_WRITER* pWriter + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +struct LOC_STRING +{ + LPWSTR wzId; + LPWSTR wzText; + BOOL bOverridable; +}; + +const int LOC_CONTROL_NOT_SET = INT_MAX; + +struct LOC_CONTROL +{ + LPWSTR wzControl; + int nX; + int nY; + int nWidth; + int nHeight; + LPWSTR wzText; +}; + +const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX; + +struct WIX_LOCALIZATION +{ + DWORD dwLangId; + + DWORD cLocStrings; + LOC_STRING* rgLocStrings; + + DWORD cLocControls; + LOC_CONTROL* rgLocControls; +}; + +/******************************************************************** + LocProbeForFile - Searches for a localization file on disk. + +*******************************************************************/ +HRESULT DAPI LocProbeForFile( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath + ); + +/******************************************************************** + LocLoadFromFile - Loads a localization file + +*******************************************************************/ +HRESULT DAPI LocLoadFromFile( + __in_z LPCWSTR wzWxlFile, + __out WIX_LOCALIZATION** ppWixLoc + ); + +/******************************************************************** + LocLoadFromResource - loads a localization file from a module's data + resource. + + NOTE: The resource data must be UTF-8 encoded. +*******************************************************************/ +HRESULT DAPI LocLoadFromResource( + __in HMODULE hModule, + __in_z LPCSTR szResource, + __out WIX_LOCALIZATION** ppWixLoc + ); + +/******************************************************************** + LocFree - free memory allocated when loading a localization file + +*******************************************************************/ +void DAPI LocFree( + __in_opt WIX_LOCALIZATION* pWixLoc + ); + +/******************************************************************** + LocLocalizeString - replace any #(loc.id) in a string with the + correct sub string +*******************************************************************/ +HRESULT DAPI LocLocalizeString( + __in const WIX_LOCALIZATION* pWixLoc, + __inout LPWSTR* psczInput + ); + +/******************************************************************** + LocGetControl - returns a control's localization information +*******************************************************************/ +HRESULT DAPI LocGetControl( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_CONTROL** ppLocControl + ); + +/******************************************************************** +LocGetString - returns a string's localization information +*******************************************************************/ +extern "C" HRESULT DAPI LocGetString( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_STRING** ppLocString + ); + +/******************************************************************** +LocAddString - adds a localization string +*******************************************************************/ +extern "C" HRESULT DAPI LocAddString( + __in WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzLocString, + __in BOOL bOverridable + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define LogExitOnFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#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; } + +#define LogExitOnFailure(x, i, f, ...) LogExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) +#define LogExitOnRootFailure(x, i, f, ...) LogExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) + +typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)( + __in_z LPCSTR szString, + __in_opt LPVOID pvContext + ); + +// enums + +// structs + +// functions +BOOL DAPI IsLogInitialized(); + +BOOL DAPI IsLogOpen(); + +void DAPI LogInitialize( + __in HMODULE hModule + ); + +HRESULT DAPI LogOpen( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzLog, + __in_z_opt LPCWSTR wzPostfix, + __in_z_opt LPCWSTR wzExt, + __in BOOL fAppend, + __in BOOL fHeader, + __out_z_opt LPWSTR* psczLogPath + ); + +void DAPI LogDisable(); + +void DAPI LogRedirect( + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, + __in_opt LPVOID pvContext + ); + +HRESULT DAPI LogRename( + __in_z LPCWSTR wzNewPath + ); + +void DAPI LogClose( + __in BOOL fFooter + ); + +void DAPI LogUninitialize( + __in BOOL fFooter + ); + +BOOL DAPI LogIsOpen(); + +HRESULT DAPI LogSetSpecialParams( + __in_z_opt LPCWSTR wzSpecialBeginLine, + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, + __in_z_opt LPCWSTR wzSpecialEndLine + ); + +REPORT_LEVEL DAPI LogSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fLogChange + ); + +REPORT_LEVEL DAPI LogGetLevel(); + +HRESULT DAPI LogGetPath( + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, + __in DWORD cchLogPath + ); + +HANDLE DAPI LogGetHandle(); + +HRESULT DAPIV LogString( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogStringArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPIV LogStringLine( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogStringLineArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPI LogIdModuleArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in va_list args + ); + +/* + * Wraps LogIdModuleArgs, so inline to save the function call + */ + +inline HRESULT LogId( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, dwLogId); + hr = LogIdModuleArgs(rl, dwLogId, NULL, args); + va_end(args); + + return hr; +} + + +/* + * Wraps LogIdModuleArgs, so inline to save the function call + */ + +inline HRESULT LogIdArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in va_list args + ) +{ + return LogIdModuleArgs(rl, dwLogId, NULL, args); +} + +HRESULT DAPIV LogErrorString( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogErrorStringArgs( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPI LogErrorIdModule( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzString1, + __in_z_opt LPCWSTR wzString2, + __in_z_opt LPCWSTR wzString3 + ); + +inline HRESULT LogErrorId( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_z_opt LPCWSTR wzString1 = NULL, + __in_z_opt LPCWSTR wzString2 = NULL, + __in_z_opt LPCWSTR wzString3 = NULL + ) +{ + return LogErrorIdModule(hrError, dwLogId, NULL, wzString1, wzString2, wzString3); +} + +HRESULT DAPI LogHeader(); + +HRESULT DAPI LogFooter(); + +HRESULT LogStringWorkRaw( + __in_z LPCSTR szLogData + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseMem(p) if (p) { MemFree(p); } +#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; } + +HRESULT DAPI MemInitialize(); +void DAPI MemUninitialize(); + +LPVOID DAPI MemAlloc( + __in SIZE_T cbSize, + __in BOOL fZero + ); +LPVOID DAPI MemReAlloc( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero + ); +HRESULT DAPI MemReAllocSecure( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero, + __deref_out LPVOID* ppvNew + ); +HRESULT DAPI MemAllocArray( + __inout LPVOID* ppvArray, + __in SIZE_T cbArrayType, + __in DWORD dwItemCount + ); +HRESULT DAPI MemReAllocArray( + __inout LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwNewItemCount + ); +HRESULT DAPI MemEnsureArraySize( + __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ); +HRESULT DAPI MemInsertIntoArray( + __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __in DWORD dwInsertIndex, + __in DWORD cInsertItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ); +void DAPI MemRemoveFromArray( + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __in DWORD dwRemoveIndex, + __in DWORD cRemoveItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in BOOL fPreserveOrder + ); +void DAPI MemArraySwapItems( + __inout_bcount(cbArrayType) LPVOID pvArray, + __in DWORD dwIndex1, + __in DWORD dwIndex2, + __in SIZE_T cbArrayType + ); + +HRESULT DAPI MemFree( + __in LPVOID pv + ); +SIZE_T DAPI MemSize( + __in LPCVOID pv + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + +// prototypes +HRESULT DAPI MetaFindWebBase( + __in IMSAdminBaseW* piMetabase, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); +HRESULT DAPI MetaFindFreeWebBase( + __in IMSAdminBaseW* piMetabase, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); + +HRESULT DAPI MetaOpenKey( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __in DWORD dwAccess, + __in DWORD cRetries, + __out METADATA_HANDLE* pmh + ); +HRESULT DAPI MetaGetValue( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __inout METADATA_RECORD* pmr + ); +void DAPI MetaFreeValue( + __in METADATA_RECORD* pmr + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseMon(mh) if (mh) { MonDestroy(mh); } +#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; } + +typedef void* MON_HANDLE; +typedef const void* C_MON_HANDLE; + +// Defined in regutil.h +enum REG_KEY_BITNESS; + +extern const int MON_HANDLE_BYTES; + +// Note: callbacks must be implemented in a thread-safe manner. They will be called asynchronously by a MonUtil-spawned thread. +// They must also be written to return as soon as possible - they are called from the waiter thread +typedef void (*PFN_MONGENERAL)( + __in HRESULT hr, + __in_opt LPVOID pvContext + ); +// This callback is not specific to any wait - it will notify client of any drive status changes, such as removable drive insertion / removal +typedef void (*PFN_MONDRIVESTATUS)( + __in WCHAR chDrive, + __in BOOL fArriving, + __in_opt LPVOID pvContext + ); +// 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, +// 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, +// so all consumers of MonUtil should be designed with this in mind. +typedef void (*PFN_MONDIRECTORY)( + __in HRESULT hr, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvDirectoryContext + ); +typedef void (*PFN_MONREGKEY)( + __in HRESULT hr, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvRegKeyContext + ); + +// Silence period allows you to avoid lots of notifications when a lot of writes are going on in a directory +// MonUtil will wait until the directory has been "silent" for at least dwSilencePeriodInMs milliseconds +// The drawback to setting this to a value higher than zero is that even single write notifications +// are delayed by this amount +HRESULT DAPI MonCreate( + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, + __in PFN_MONGENERAL vpfMonGeneral, + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, + __in_opt PFN_MONDIRECTORY vpfMonDirectory, + __in_opt PFN_MONREGKEY vpfMonRegKey, + __in_opt LPVOID pvContext + ); +// Don't add multiple identical waits! Not only is it wasteful and will cause multiple fires for the exact same change, it will also +// result in slightly odd behavior when you remove a duplicated wait (removing a wait may or may not remove multiple waits) +// 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. +// So instead, de-dupe your wait requests before sending them to MonUtil. +// Special notes for network waits: MonUtil can send false positive notifications (i.e. notifications when nothing had changed) if connection +// to the share is lost and reconnected, because MonUtil can't know for sure whether changes occurred while the connection was lost. +// Also, MonUtil will very every 20 minutes retry even successful network waits, because the underlying Win32 API cannot notify us if a remote server +// had its network cable unplugged or similar sudden failure. When we retry the successful network waits, we will also send a false positive notification, +// 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 +// 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. +HRESULT DAPI MonAddDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvDirectoryContext + ); +HRESULT DAPI MonAddRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvRegKeyContext + ); +HRESULT DAPI MonRemoveDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive + ); +HRESULT DAPI MonRemoveRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive + ); +void DAPI MonDestroy( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum OS_VERSION +{ + OS_VERSION_UNKNOWN, + OS_VERSION_WINNT, + OS_VERSION_WIN2000, + OS_VERSION_WINXP, + OS_VERSION_WIN2003, + OS_VERSION_VISTA, + OS_VERSION_WIN2008, + OS_VERSION_WIN7, + OS_VERSION_WIN2008_R2, + OS_VERSION_FUTURE +} OS_VERSION; + +void DAPI OsGetVersion( + __out OS_VERSION* pVersion, + __out DWORD* pdwServicePack + ); +HRESULT DAPI OsCouldRunPrivileged( + __out BOOL* pfPrivileged + ); +HRESULT DAPI OsIsRunningPrivileged( + __out BOOL* pfPrivileged + ); +HRESULT DAPI OsIsUacEnabled( + __out BOOL* pfUacEnabled + ); +HRESULT DAPI OsRtlGetVersion( + __inout RTL_OSVERSIONINFOEXW* pOvix + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum PATH_EXPAND +{ + PATH_EXPAND_ENVIRONMENT = 0x0001, + PATH_EXPAND_FULLPATH = 0x0002, +} PATH_EXPAND; + + +/******************************************************************* + PathCommandLineAppend - appends a command line argument on to a + string such that ::CommandLineToArgv() will shred them correctly + (i.e. quote arguments with spaces in them). +********************************************************************/ +DAPI_(HRESULT) PathCommandLineAppend( + __deref_inout_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ); + +/******************************************************************* + PathFile - returns a pointer to the file part of the path. +********************************************************************/ +DAPI_(LPWSTR) PathFile( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathExtension - returns a pointer to the extension part of the path + (including the dot). +********************************************************************/ +DAPI_(LPCWSTR) PathExtension( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathGetDirectory - extracts the directory from a path. +********************************************************************/ +DAPI_(HRESULT) PathGetDirectory( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczDirectory + ); + +/******************************************************************* +PathGetParentPath - extracts the parent directory from a full path. +********************************************************************/ +DAPI_(HRESULT) PathGetParentPath( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczDirectory + ); + +/******************************************************************* + PathExpand - gets the full path to a file resolving environment + variables along the way. +********************************************************************/ +DAPI_(HRESULT) PathExpand( + __out LPWSTR *psczFullPath, + __in_z LPCWSTR wzRelativePath, + __in DWORD dwResolveFlags + ); + +/******************************************************************* + PathPrefix - prefixes a full path with \\?\ or \\?\UNC as + appropriate. +********************************************************************/ +DAPI_(HRESULT) PathPrefix( + __inout LPWSTR *psczFullPath + ); + +/******************************************************************* + PathFixedBackslashTerminate - appends a \ if path does not have it + already, but fails if the buffer is + insufficient. +********************************************************************/ +DAPI_(HRESULT) PathFixedBackslashTerminate( + __inout_ecount_z(cchPath) LPWSTR wzPath, + __in SIZE_T cchPath + ); + +/******************************************************************* + PathBackslashTerminate - appends a \ if path does not have it + already. +********************************************************************/ +DAPI_(HRESULT) PathBackslashTerminate( + __inout LPWSTR* psczPath + ); + +/******************************************************************* + PathForCurrentProcess - gets the full path to the currently executing + process or (optionally) a module inside the process. +********************************************************************/ +DAPI_(HRESULT) PathForCurrentProcess( + __inout LPWSTR *psczFullPath, + __in_opt HMODULE hModule + ); + +/******************************************************************* + PathRelativeToModule - gets the name of a file in the same + directory as the current process or (optionally) a module inside + the process +********************************************************************/ +DAPI_(HRESULT) PathRelativeToModule( + __inout LPWSTR *psczFullPath, + __in_opt LPCWSTR wzFileName, + __in_opt HMODULE hModule + ); + +/******************************************************************* + PathCreateTempFile + + Note: if wzDirectory is null, ::GetTempPath() will be used instead. + if wzFileNameTemplate is null, GetTempFileName() will be used instead. +*******************************************************************/ +DAPI_(HRESULT) PathCreateTempFile( + __in_opt LPCWSTR wzDirectory, + __in_opt __format_string LPCWSTR wzFileNameTemplate, + __in DWORD dwUniqueCount, + __in DWORD dwFileAttributes, + __out_opt LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ); + +/******************************************************************* + PathCreateTimeBasedTempFile - creates an empty temp file based on current + system time +********************************************************************/ +DAPI_(HRESULT) PathCreateTimeBasedTempFile( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzPrefix, + __in_z_opt LPCWSTR wzPostfix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ); + +/******************************************************************* + PathCreateTempDirectory + + Note: if wzDirectory is null, ::GetTempPath() will be used instead. +*******************************************************************/ +DAPI_(HRESULT) PathCreateTempDirectory( + __in_opt LPCWSTR wzDirectory, + __in __format_string LPCWSTR wzDirectoryNameTemplate, + __in DWORD dwUniqueCount, + __out LPWSTR* psczTempDirectory + ); + +/******************************************************************* + PathGetKnownFolder - returns the path to a well-known shell folder + +*******************************************************************/ +DAPI_(HRESULT) PathGetKnownFolder( + __in int csidl, + __out LPWSTR* psczKnownFolder + ); + +/******************************************************************* + PathIsAbsolute - returns true if the path is absolute; false + otherwise. +*******************************************************************/ +DAPI_(BOOL) PathIsAbsolute( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathConcat - like .NET's Path.Combine, lets you build up a path + one piece -- file or directory -- at a time. +*******************************************************************/ +DAPI_(HRESULT) PathConcat( + __in_opt LPCWSTR wzPath1, + __in_opt LPCWSTR wzPath2, + __deref_out_z LPWSTR* psczCombined + ); + +/******************************************************************* + PathConcatCch - like .NET's Path.Combine, lets you build up a path + one piece -- file or directory -- at a time. +*******************************************************************/ +DAPI_(HRESULT) PathConcatCch( + __in_opt LPCWSTR wzPath1, + __in SIZE_T cchPath1, + __in_opt LPCWSTR wzPath2, + __in SIZE_T cchPath2, + __deref_out_z LPWSTR* psczCombined + ); + +/******************************************************************* + PathEnsureQuoted - ensures that a path is quoted; optionally, + this function also terminates a directory with a backslash + if it is not already. +*******************************************************************/ +DAPI_(HRESULT) PathEnsureQuoted( + __inout LPWSTR* ppszPath, + __in BOOL fDirectory + ); + +/******************************************************************* + PathCompare - compares the fully expanded path of the two paths using + ::CompareStringW(). +*******************************************************************/ +DAPI_(HRESULT) PathCompare( + __in_z LPCWSTR wzPath1, + __in_z LPCWSTR wzPath2, + __out int* pnResult + ); + +/******************************************************************* + PathCompress - sets the compression state on an existing file or + directory. A no-op on file systems that don't + support compression. +*******************************************************************/ +DAPI_(HRESULT) PathCompress( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathGetHierarchyArray - allocates an array containing, + in order, every parent directory of the specified path, + ending with the actual input path + This function also works with registry subkeys +*******************************************************************/ +DAPI_(HRESULT) PathGetHierarchyArray( + __in_z LPCWSTR wzPath, + __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, + __inout LPUINT pcPathArray + ); + +/******************************************************************* + PathCanonicalizePath - wrapper around PathCanonicalizeW. +*******************************************************************/ +DAPI_(HRESULT) PathCanonicalizePath( + __in_z LPCWSTR wzPath, + __deref_out_z LPWSTR* psczCanonicalized + ); + +/******************************************************************* +PathDirectoryContainsPath - checks if wzPath is located inside + wzDirectory. +*******************************************************************/ +DAPI_(HRESULT) PathDirectoryContainsPath( + __in_z LPCWSTR wzDirectory, + __in_z LPCWSTR wzPath + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + + +// functions +void DAPI PerfInitialize( + ); +void DAPI PerfClickTime( + __out_opt LARGE_INTEGER* pliElapsed + ); +double DAPI PerfConvertToSeconds( + __in const LARGE_INTEGER* pli + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn"; + +/******************************************************************** +PolcReadNumber - reads a number from policy. + +NOTE: S_FALSE returned if policy not set. +NOTE: out is set to default on S_FALSE or any error. +********************************************************************/ +HRESULT DAPI PolcReadNumber( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in DWORD dwDefault, + __out DWORD* pdw + ); + +/******************************************************************** +PolcReadString - reads a string from policy. + +NOTE: S_FALSE returned if policy not set. +NOTE: out is set to default on S_FALSE or any error. +********************************************************************/ +HRESULT DAPI PolcReadString( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in_z_opt LPCWSTR wzDefault, + __deref_out_z LPWSTR* pscz + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs +typedef struct _PROC_FILESYSTEMREDIRECTION +{ + BOOL fDisabled; + LPVOID pvRevertState; +} PROC_FILESYSTEMREDIRECTION; + +HRESULT DAPI ProcElevated( + __in HANDLE hProcess, + __out BOOL* pfElevated + ); + +HRESULT DAPI ProcWow64( + __in HANDLE hProcess, + __out BOOL* pfWow64 + ); +HRESULT DAPI ProcDisableWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ); +HRESULT DAPI ProcRevertWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ); + +HRESULT DAPI ProcExec( + __in_z LPCWSTR wzExecutablePath, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out HANDLE *phProcess + ); +HRESULT DAPI ProcExecute( + __in_z LPWSTR wzCommand, + __out HANDLE *phProcess, + __out_opt HANDLE *phChildStdIn, + __out_opt HANDLE *phChildStdOutErr + ); +HRESULT DAPI ProcWaitForCompletion( + __in HANDLE hProcess, + __in DWORD dwTimeout, + __out DWORD *pReturnCode + ); +HRESULT DAPI ProcWaitForIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds, + __in DWORD dwMilliseconds + ); +HRESULT DAPI ProcCloseIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds + ); + +// following code in proc2utl.cpp due to dependency on PSAPI.DLL. +HRESULT DAPI ProcFindAllIdsFromExeName( + __in_z LPCWSTR wzExeName, + __out DWORD** ppdwProcessIds, + __out DWORD* pcProcessIds + ); + +// following code in proc3utl.cpp due to dependency on Wtsapi32.DLL. +HRESULT DAPI ProcExecuteAsInteractiveUser( + __in_z LPCWSTR wzExecutablePath, + __in_z LPCWSTR wzCommand, + __out HANDLE *phProcess + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; } + +typedef enum REG_KEY_BITNESS +{ + REG_KEY_DEFAULT = 0, + REG_KEY_32BIT = 1, + REG_KEY_64BIT = 2 +} REG_KEY_BITNESS; + +typedef LSTATUS (APIENTRY *PFN_REGCREATEKEYEXW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ); +typedef LSTATUS (APIENTRY *PFN_REGOPENKEYEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey + ); +typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)( + __in HKEY hKey, + __in DWORD dwIndex, + __out LPWSTR lpName, + __inout LPDWORD lpcName, + __reserved LPDWORD lpReserved, + __inout_opt LPWSTR lpClass, + __inout_opt LPDWORD lpcClass, + __out_opt PFILETIME lpftLastWriteTime + ); +typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)( + __in HKEY hKey, + __in DWORD dwIndex, + __out LPWSTR lpValueName, + __inout LPDWORD lpcchValueName, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpType, + __out_opt LPBYTE lpData, + __out_opt LPDWORD lpcbData + ); +typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)( + __in HKEY hKey, + __out_opt LPWSTR lpClass, + __inout_opt LPDWORD lpcClass, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpcSubKeys, + __out_opt LPDWORD lpcMaxSubKeyLen, + __out_opt LPDWORD lpcMaxClassLen, + __out_opt LPDWORD lpcValues, + __out_opt LPDWORD lpcMaxValueNameLen, + __out_opt LPDWORD lpcMaxValueLen, + __out_opt LPDWORD lpcbSecurityDescriptor, + __out_opt PFILETIME lpftLastWriteTime + ); +typedef LSTATUS (APIENTRY *PFN_REGQUERYVALUEEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpType, + __out_bcount_part_opt(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData, + __inout_opt LPDWORD lpcbData + ); +typedef LSTATUS (APIENTRY *PFN_REGSETVALUEEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName, + __reserved DWORD Reserved, + __in DWORD dwType, + __in_bcount_opt(cbData) CONST BYTE* lpData, + __in DWORD cbData + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName + ); + +HRESULT DAPI RegInitialize(); +void DAPI RegUninitialize(); + +void DAPI RegFunctionOverride( + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW + ); +HRESULT DAPI RegCreate( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ); +HRESULT DAPI RegCreateEx( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __in BOOL fVolatile, + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, + __out HKEY* phk, + __out_opt BOOL* pfCreated + ); +HRESULT DAPI RegOpen( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ); +HRESULT DAPI RegDelete( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fDeleteTree + ); +HRESULT DAPI RegKeyEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczKey + ); +HRESULT DAPI RegValueEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczName, + __out_opt DWORD *pdwType + ); +HRESULT DAPI RegGetType( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD *pdwType + ); +HRESULT DAPI RegReadBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T *pcbBuffer + ); +HRESULT DAPI RegReadString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_z LPWSTR* psczValue + ); +HRESULT DAPI RegReadStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, + __out DWORD *pcStrings + ); +HRESULT DAPI RegReadVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pdw64Version + ); +HRESULT DAPI RegReadNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD* pdwValue + ); +HRESULT DAPI RegReadQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pqwValue + ); +HRESULT DAPI RegWriteBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_bcount(cbBuffer) const BYTE *pbBuffer, + __in DWORD cbBuffer + ); +HRESULT DAPI RegWriteString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI RegWriteStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_ecount(cStrings) LPWSTR *rgwzStrings, + __in DWORD cStrings + ); +HRESULT DAPI RegWriteStringFormatted( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in __format_string LPCWSTR szFormat, + ... + ); +HRESULT DAPI RegWriteNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD dwValue + ); +HRESULT DAPI RegWriteQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD64 qwValue + ); +HRESULT DAPI RegQueryKey( + __in HKEY hk, + __out_opt DWORD* pcSubKeys, + __out_opt DWORD* pcValues + ); +HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ); +BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI ResGetStringLangId( + __in_opt LPCWSTR wzPath, + __in UINT uID, + __out WORD *pwLangId + ); + +HRESULT DAPI ResReadString( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPWSTR* ppwzString + ); + +HRESULT DAPI ResReadStringAnsi( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPSTR* ppszString + ); + +HRESULT DAPI ResReadData( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szDataName, + __deref_out_bcount(*pcb) PVOID *ppv, + __out DWORD *pcb + ); + +HRESULT DAPI ResExportDataToFile( + __in_z LPCSTR szDataName, + __in_z LPCWSTR wzTargetFile, + __in DWORD dwCreationDisposition + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI ResWriteString( + __in_z LPCWSTR wzResourceFile, + __in DWORD dwDataId, + __in_z LPCWSTR wzData, + __in WORD wLangId + ); + +HRESULT DAPI ResWriteData( + __in_z LPCWSTR wzResourceFile, + __in_z LPCSTR szDataName, + __in PVOID pData, + __in DWORD cbData + ); + +HRESULT DAPI ResImportDataFromFile( + __in_z LPCWSTR wzTargetFile, + __in_z LPCWSTR wzSourceFile, + __in_z LPCSTR szDataName + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// defines +#define FILETABLESIZE 40 + +// structs +struct MEM_FILE +{ + LPCBYTE vpStart; + UINT uiCurrent; + UINT uiLength; +}; + +typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE; + +typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); +typedef VOID (*REX_CALLBACK_WRITE)(UINT cb); + + +struct FAKE_FILE // used __in internal file table +{ + BOOL fUsed; + FAKE_FILE_TYPE fftType; + MEM_FILE mfFile; // State for memory file + HANDLE hFile; // Handle for disk file +}; + +// functions +HRESULT RexInitialize(); +void RexUninitialize(); + +HRESULT RexExtract( + __in_z LPCSTR szResource, + __in_z LPCWSTR wzExtractId, + __in_z LPCWSTR wzExtractDir, + __in_z LPCWSTR wzExtractName, + __in REX_CALLBACK_PROGRESS pfnProgress, + __in REX_CALLBACK_WRITE pfnWrite, + __in LPVOID pvContext + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _RMU_SESSION *PRMU_SESSION; + +HRESULT DAPI RmuJoinSession( + __out PRMU_SESSION *ppSession, + __in_z LPCWSTR wzSessionKey + ); + +HRESULT DAPI RmuAddFile( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzPath + ); + +HRESULT DAPI RmuAddProcessById( + __in PRMU_SESSION pSession, + __in DWORD dwProcessId + ); + +HRESULT DAPI RmuAddProcessesByName( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzProcessName + ); + +HRESULT DAPI RmuAddService( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzServiceName + ); + +HRESULT DAPI RmuRegisterResources( + __in PRMU_SESSION pSession + ); + +HRESULT DAPI RmuEndSession( + __in PRMU_SESSION pSession + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); } +#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; } + + +struct RSS_UNKNOWN_ATTRIBUTE +{ + LPWSTR wzNamespace; + LPWSTR wzAttribute; + LPWSTR wzValue; + + RSS_UNKNOWN_ATTRIBUTE* pNext; +}; + +struct RSS_UNKNOWN_ELEMENT +{ + LPWSTR wzNamespace; + LPWSTR wzElement; + LPWSTR wzValue; + + RSS_UNKNOWN_ATTRIBUTE* pAttributes; + RSS_UNKNOWN_ELEMENT* pNext; +}; + +struct RSS_ITEM +{ + LPWSTR wzTitle; + LPWSTR wzLink; + LPWSTR wzDescription; + + LPWSTR wzGuid; + FILETIME ftPublished; + + LPWSTR wzEnclosureUrl; + DWORD dwEnclosureSize; + LPWSTR wzEnclosureType; + + RSS_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct RSS_CHANNEL +{ + LPWSTR wzTitle; + LPWSTR wzLink; + LPWSTR wzDescription; + DWORD dwTimeToLive; + + RSS_UNKNOWN_ELEMENT* pUnknownElements; + + DWORD cItems; + RSS_ITEM rgItems[1]; +}; + +HRESULT DAPI RssInitialize( + ); + +void DAPI RssUninitialize( + ); + +HRESULT DAPI RssParseFromString( + __in_z LPCWSTR wzRssString, + __out RSS_CHANNEL **ppChannel + ); + +HRESULT DAPI RssParseFromFile( + __in_z LPCWSTR wzRssFile, + __out RSS_CHANNEL **ppChannel + ); + +// Adding this until we have the updated specstrings.h +#ifndef __in_xcount +#define __in_xcount(size) +#endif + +void DAPI RssFreeChannel( + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef void* SCE_DATABASE_HANDLE; +typedef void* SCE_ROW_HANDLE; +typedef void* SCE_QUERY_HANDLE; +typedef void* SCE_QUERY_RESULTS_HANDLE; + +extern const int SCE_ROW_HANDLE_BYTES; +extern const int SCE_QUERY_HANDLE_BYTES; +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES; + +#define ReleaseSceRow(prrh) if (prrh) { SceFreeRow(prrh); } +#define ReleaseNullSceRow(prrh) if (prrh) { SceFreeRow(prrh); prrh = NULL; } +#define ReleaseSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); } +#define ReleaseNullSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); pqh = NULL; } +#define ReleaseSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); } +#define ReleaseNullSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); pqh = NULL; } + +struct SCE_COLUMN_SCHEMA +{ + LPCWSTR wzName; + DBTYPE dbtColumnType; + DWORD dwLength; + BOOL fPrimaryKey; // If this column is the primary key + BOOL fNullable; + BOOL fAutoIncrement; + BOOL fDescending; // If this column should be descending when used in an index (default is ascending) + + LPWSTR wzRelationName; + DWORD dwForeignKeyTable; + DWORD dwForeignKeyColumn; +}; + +struct SCE_INDEX_SCHEMA +{ + LPWSTR wzName; + + DWORD *rgColumns; + DWORD cColumns; +}; + +struct SCE_TABLE_SCHEMA +{ + LPCWSTR wzName; + DWORD cColumns; + SCE_COLUMN_SCHEMA *rgColumns; + + DWORD cIndexes; + SCE_INDEX_SCHEMA *rgIndexes; + + // Internal to SCEUtil - consumers shouldn't access or modify + // TODO: enforce / hide in a handle of some sort? + IRowset *pIRowset; + IRowsetChange *pIRowsetChange; +}; + +struct SCE_DATABASE_SCHEMA +{ + DWORD cTables; + SCE_TABLE_SCHEMA *rgTables; +}; + +struct SCE_DATABASE +{ + SCE_DATABASE_HANDLE sdbHandle; + SCE_DATABASE_SCHEMA *pdsSchema; +}; + +HRESULT DAPI SceCreateDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __deref_out SCE_DATABASE **ppDatabase + ); +HRESULT DAPI SceOpenDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __deref_out SCE_DATABASE **ppDatabase, + __in BOOL fReadOnly + ); +HRESULT DAPI SceEnsureDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __in SCE_DATABASE_SCHEMA *pdsSchema, + __deref_out SCE_DATABASE **ppDatabase + ); +HRESULT DAPI SceIsTableEmpty( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out BOOL *pfEmpty + ); +HRESULT DAPI SceGetFirstRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceGetNextRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceBeginTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceCommitTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceRollbackTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceDeleteRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI ScePrepareInsert( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceFinishUpdate( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ); +HRESULT DAPI SceSetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI SceSetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD dwValue + ); +HRESULT DAPI SceSetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD64 qwValue + ); +HRESULT DAPI SceSetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const BOOL fValue + ); +HRESULT DAPI SceSetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI SceSetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const SYSTEMTIME *pst + ); +HRESULT DAPI SceSetColumnNull( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex + ); +HRESULT DAPI SceGetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbBuffer, + __inout SIZE_T *pcbBuffer + ); +HRESULT DAPI SceGetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD *pdwValue + ); +HRESULT DAPI SceGetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD64 *pqwValue + ); +HRESULT DAPI SceGetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out BOOL *pfValue + ); +HRESULT DAPI SceGetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_z LPWSTR *psczValue + ); +HRESULT DAPI SceGetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out SYSTEMTIME *pst + ); +HRESULT DAPI SceBeginQuery( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __in DWORD dwIndex, + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle + ); +HRESULT DAPI SceSetQueryColumnBinary( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI SceSetQueryColumnDword( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD dwValue + ); +HRESULT DAPI SceSetQueryColumnQword( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD64 qwValue + ); +HRESULT DAPI SceSetQueryColumnBool( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const BOOL fValue + ); +HRESULT DAPI SceSetQueryColumnString( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_z_opt LPCWSTR wzString + ); +HRESULT DAPI SceSetQueryColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in const SYSTEMTIME *pst + ); +HRESULT DAPI SceSetQueryColumnEmpty( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle + ); +HRESULT DAPI SceRunQueryExact( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceRunQueryRange( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle + ); +HRESULT DAPI SceGetNextResultRow( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +void DAPI SceCloseTable( + __in SCE_TABLE_SCHEMA *pTable + ); +// Returns whether the data in the database changed. Ignores schema changes. +BOOL DAPI SceDatabaseChanged( + __in SCE_DATABASE *pDatabase + ); +// Resets the database changed flag +void DAPI SceResetDatabaseChanged( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceCloseDatabase( + __in SCE_DATABASE *pDatabase + ); +void DAPI SceFreeRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle + ); +void DAPI SceFreeQuery( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle + ); +void DAPI SceFreeQueryResults( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +class PSCZ +{ +public: + PSCZ() : m_scz(NULL) { } + + ~PSCZ() { ReleaseNullStr(m_scz); } + + operator LPWSTR() { return m_scz; } + + operator LPCWSTR() { return m_scz; } + + operator bool() { return NULL != m_scz; } + + LPWSTR* operator &() { return &m_scz; } + + bool operator !() { return !m_scz; } + + WCHAR operator *() { return *m_scz; } + + LPWSTR Detach() { LPWSTR scz = m_scz; m_scz = NULL; return scz; } + +private: + LPWSTR m_scz; +}; +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifndef REFKNOWNFOLDERID +#define REFKNOWNFOLDERID REFGUID +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ); + +void DAPI ShelFunctionOverride( + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW + ); +HRESULT DAPI ShelExec( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd, + __in_opt HWND hwndParent, + __out_opt HANDLE* phProcess + ); +HRESULT DAPI ShelExecUnelevated( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd + ); +HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ); +HRESULT DAPI ShelGetKnownFolder( + __out_z LPWSTR* psczFolderPath, + __in REFKNOWNFOLDERID rfidFolder + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +// Adding this until the SQL annotations are published to specstrings.h +#ifndef __sql_command +#define __sql_command +#endif + +// structs +struct SQL_FILESPEC +{ + WCHAR wzName[MAX_PATH]; + WCHAR wzFilename[MAX_PATH]; + WCHAR wzSize[MAX_PATH]; + WCHAR wzMaxSize[MAX_PATH]; + WCHAR wzGrow[MAX_PATH]; +}; + + +// functions +HRESULT DAPI SqlConnectDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out IDBCreateSession** ppidbSession + ); +HRESULT DAPI SqlStartTransaction( + __in IDBCreateSession* pidbSession, + __out IDBCreateCommand** ppidbCommand, + __out ITransaction** ppit + ); +HRESULT DAPI SqlEndTransaction( + __in ITransaction* pit, + __in BOOL fCommit + ); +HRESULT DAPI SqlDatabaseExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDatabaseExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlDatabaseEnsureExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDatabaseEnsureExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlCreateDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionCreateDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlDropDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDropDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionExecuteQuery( + __in IDBCreateSession* pidbSession, + __in __sql_command LPCWSTR wzSql, + __out_opt IRowset** ppirs, + __out_opt DBROWCOUNT* pcRows, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlCommandExecuteQuery( + __in IDBCreateCommand* pidbCommand, + __in __sql_command LPCWSTR wzSql, + __out IRowset** ppirs, + __out DBROWCOUNT* pcRows + ); +HRESULT DAPI SqlGetErrorInfo( + __in IUnknown* pObjectWithError, + __in REFIID IID_InterfaceWithError, + __in DWORD dwLocaleId, + __out_opt BSTR* pbstrErrorSource, + __out_opt BSTR* pbstrErrorDescription + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum SRP_ACTION +{ + SRP_ACTION_UNKNOWN, + SRP_ACTION_UNINSTALL, + SRP_ACTION_INSTALL, + SRP_ACTION_MODIFY, +} SRP_ACTION; + + +/******************************************************************** + SrpInitialize - initializes system restore point functionality. + +*******************************************************************/ +DAPI_(HRESULT) SrpInitialize( + __in BOOL fInitializeComSecurity + ); + +/******************************************************************** + SrpUninitialize - uninitializes system restore point functionality. + +*******************************************************************/ +DAPI_(void) SrpUninitialize(); + +/******************************************************************** + SrpCreateRestorePoint - creates a system restore point. + +*******************************************************************/ +DAPI_(HRESULT) SrpCreateRestorePoint( + __in_z LPCWSTR wzApplicationName, + __in SRP_ACTION action + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseStr(pwz) if (pwz) { StrFree(pwz); } +#define ReleaseNullStr(pwz) if (pwz) { StrFree(pwz); pwz = NULL; } +#define ReleaseBSTR(bstr) if (bstr) { ::SysFreeString(bstr); } +#define ReleaseNullBSTR(bstr) if (bstr) { ::SysFreeString(bstr); bstr = NULL; } +#define ReleaseStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); } } +#define ReleaseNullStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); c = 0; rg = NULL; } } +#define ReleaseNullStrSecure(pwz) if (pwz) { StrSecureZeroFreeString(pwz); pwz = NULL; } + +#define DeclareConstBSTR(bstr_const, wz) const WCHAR bstr_const[] = { 0x00, 0x00, sizeof(wz)-sizeof(WCHAR), 0x00, wz } +#define UseConstBSTR(bstr_const) const_cast(bstr_const + 4) + +HRESULT DAPI StrAlloc( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ); +HRESULT DAPI StrAllocSecure( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ); +HRESULT DAPI StrTrimCapacity( + __deref_out_z LPWSTR* ppwz + ); +HRESULT DAPI StrTrimWhitespace( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource + ); +HRESULT DAPI StrAnsiAlloc( + __deref_out_ecount_part(cch, 0) LPSTR* ppz, + __in SIZE_T cch + ); +HRESULT DAPI StrAnsiTrimCapacity( + __deref_out_z LPSTR* ppz + ); +HRESULT DAPI StrAnsiTrimWhitespace( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR szSource + ); +HRESULT DAPI StrAllocString( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocStringSecure( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAnsiAllocString( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ); +HRESULT DAPI StrAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ); +HRESULT DAPI StrAnsiAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocPrefix( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzPrefix, + __in SIZE_T cchPrefix + ); +HRESULT DAPI StrAllocConcat( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocConcatSecure( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAnsiAllocConcat( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR pzSource, + __in SIZE_T cchSource + ); +HRESULT __cdecl StrAllocFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocConcatFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocConcatFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAnsiAllocFormatted( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI StrAllocFormattedArgs( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +HRESULT DAPI StrAllocFormattedArgsSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +HRESULT DAPI StrAnsiAllocFormattedArgs( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + __in va_list args + ); +HRESULT DAPI StrAllocFromError( + __inout LPWSTR *ppwzMessage, + __in HRESULT hrError, + __in_opt HMODULE hModule, + ... + ); + +HRESULT DAPI StrMaxLength( + __in LPCVOID p, + __out SIZE_T* pcbch + ); +HRESULT DAPI StrSize( + __in LPCVOID p, + __out SIZE_T* pcbb + ); + +HRESULT DAPI StrFree( + __in LPVOID p + ); + + +HRESULT DAPI StrReplaceStringAll( + __inout LPWSTR* ppwzOriginal, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ); +HRESULT DAPI StrReplaceString( + __inout LPWSTR* ppwzOriginal, + __inout DWORD* pdwStartIndex, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ); + +HRESULT DAPI StrHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out_ecount(cchDest) LPWSTR wzDest, + __in SIZE_T cchDest + ); +HRESULT DAPI StrAllocHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest + ); +HRESULT DAPI StrHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(cbDest) BYTE* pbDest, + __in SIZE_T cbDest + ); +HRESULT DAPI StrAllocHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(*pcbDest) BYTE** ppbDest, + __out_opt DWORD* pcbDest + ); + +HRESULT DAPI StrAllocBase85Encode( + __in_bcount_opt(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_z LPWSTR* pwzDest + ); +HRESULT DAPI StrAllocBase85Decode( + __in_z LPCWSTR wzSource, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out SIZE_T* pcbDest +); + +HRESULT DAPI MultiSzLen( + __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, + __out SIZE_T* pcch + ); +HRESULT DAPI MultiSzPrepend( + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in __nullnullterminated LPCWSTR pwzInsert + ); +HRESULT DAPI MultiSzFindSubstring( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzSubstring, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn + ); +HRESULT DAPI MultiSzFindString( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzString, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound + ); +HRESULT DAPI MultiSzRemoveString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex + ); +HRESULT DAPI MultiSzInsertString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzInsert + ); +HRESULT DAPI MultiSzReplaceString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzString + ); + +LPCWSTR DAPI wcsistr( + __in_z LPCWSTR wzString, + __in_z LPCWSTR wzCharSet + ); + +HRESULT DAPI StrStringToInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out SHORT* psOut + ); +HRESULT DAPI StrStringToUInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out USHORT* pusOut + ); +HRESULT DAPI StrStringToInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out INT* piOut + ); +HRESULT DAPI StrStringToUInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out UINT* puiOut + ); +HRESULT DAPI StrStringToInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out LONGLONG* pllOut + ); +HRESULT DAPI StrStringToUInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out ULONGLONG* pullOut + ); +void DAPI StrStringToUpper( + __inout_z LPWSTR wzIn + ); +void DAPI StrStringToLower( + __inout_z LPWSTR wzIn + ); +HRESULT DAPI StrAllocStringToUpperInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocStringToLowerInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); + +HRESULT DAPI StrArrayAllocString( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); + +HRESULT DAPI StrArrayFree( + __in_ecount(cStrArray) LPWSTR *rgsczStrArray, + __in UINT cStrArray + ); + +HRESULT DAPI StrSplitAllocArray( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzDelim + ); + +HRESULT DAPI StrSecureZeroString( + __in LPWSTR pwz + ); +HRESULT DAPI StrSecureZeroFreeString( + __in LPWSTR pwz + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; } + + +HRESULT DAPI SvcQueryConfig( + __in SC_HANDLE sch, + __out QUERY_SERVICE_CONFIGW** ppConfig + ); + + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; } + +typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)( + __in_z LPCWSTR wzCondition, + __out BOOL* pf, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)( + __in_z LPCWSTR wzFormat, + __inout LPWSTR* psczOut, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)( + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)( + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)( + __in_z LPCWSTR wzVariable, + __inout LPWSTR* psczValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)( + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fFormatted, + __in_opt LPVOID pvContext + ); + +typedef enum THEME_ACTION_TYPE +{ + THEME_ACTION_TYPE_BROWSE_DIRECTORY, + THEME_ACTION_TYPE_CHANGE_PAGE, + THEME_ACTION_TYPE_CLOSE_WINDOW, +} THEME_ACTION_TYPE; + +typedef enum THEME_CONTROL_DATA +{ + THEME_CONTROL_DATA_HOVER = 1, +} THEME_CONTROL_DATA; + +typedef enum THEME_CONTROL_TYPE +{ + THEME_CONTROL_TYPE_UNKNOWN, + THEME_CONTROL_TYPE_BILLBOARD, + THEME_CONTROL_TYPE_BUTTON, + THEME_CONTROL_TYPE_CHECKBOX, + THEME_CONTROL_TYPE_COMBOBOX, + THEME_CONTROL_TYPE_COMMANDLINK, + THEME_CONTROL_TYPE_EDITBOX, + THEME_CONTROL_TYPE_HYPERLINK, + THEME_CONTROL_TYPE_HYPERTEXT, + THEME_CONTROL_TYPE_IMAGE, + THEME_CONTROL_TYPE_LABEL, + THEME_CONTROL_TYPE_PANEL, + THEME_CONTROL_TYPE_PROGRESSBAR, + THEME_CONTROL_TYPE_RADIOBUTTON, + THEME_CONTROL_TYPE_RICHEDIT, + THEME_CONTROL_TYPE_STATIC, + THEME_CONTROL_TYPE_LISTVIEW, + THEME_CONTROL_TYPE_TREEVIEW, + THEME_CONTROL_TYPE_TAB, +} THEME_CONTROL_TYPE; + +typedef enum THEME_SHOW_PAGE_REASON +{ + THEME_SHOW_PAGE_REASON_DEFAULT, + THEME_SHOW_PAGE_REASON_CANCEL, + THEME_SHOW_PAGE_REASON_REFRESH, +} THEME_SHOW_PAGE_REASON; + +typedef enum THEME_WINDOW_INITIAL_POSITION +{ + THEME_WINDOW_INITIAL_POSITION_DEFAULT, + THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, +} THEME_WINDOW_INITIAL_POSITION; + + +struct THEME_COLUMN +{ + LPWSTR pszName; + UINT uStringId; + int nDefaultDpiBaseWidth; + int nBaseWidth; + int nWidth; + BOOL fExpands; +}; + + +struct THEME_TAB +{ + LPWSTR pszName; + UINT uStringId; +}; + +struct THEME_ACTION +{ + LPWSTR sczCondition; + THEME_ACTION_TYPE type; + union + { + struct + { + LPWSTR sczVariableName; + } BrowseDirectory; + struct + { + LPWSTR sczPageName; + BOOL fCancel; + } ChangePage; + }; +}; + +struct THEME_CONDITIONAL_TEXT +{ + LPWSTR sczCondition; + LPWSTR sczText; +}; + +// THEME_ASSIGN_CONTROL_ID - Used to apply a specific id to a named control (usually +// to set the WM_COMMAND). +struct THEME_ASSIGN_CONTROL_ID +{ + WORD wId; // id to apply to control + LPCWSTR wzName; // name of control to match +}; + +const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned. + +struct THEME_CONTROL +{ + THEME_CONTROL_TYPE type; + + WORD wId; + WORD wPageId; + + LPWSTR sczName; // optional name for control, used to apply control id and link the control to a variable. + LPWSTR sczText; + LPWSTR sczTooltip; + LPWSTR sczNote; // optional text for command link + int nDefaultDpiX; + int nDefaultDpiY; + int nDefaultDpiHeight; + int nDefaultDpiWidth; + int nX; + int nY; + int nHeight; + int nWidth; + int nSourceX; + int nSourceY; + UINT uStringId; + + LPWSTR sczEnableCondition; + LPWSTR sczVisibleCondition; + BOOL fDisableVariableFunctionality; + + HBITMAP hImage; + HICON hIcon; + + // Don't free these; it's just a handle to the central image lists stored in THEME. The handle is freed once, there. + HIMAGELIST rghImageList[4]; + + DWORD dwStyle; + DWORD dwExtendedStyle; + DWORD dwInternalStyle; + + DWORD dwFontId; + + // child controls + DWORD cControls; + THEME_CONTROL* rgControls; + + // Used by billboard controls + WORD wBillboardInterval; + BOOL fBillboardLoops; + + // Used by button and command link controls + THEME_ACTION* rgActions; + DWORD cActions; + THEME_ACTION* pDefaultAction; + + // Used by hyperlink and owner-drawn button controls + DWORD dwFontHoverId; + DWORD dwFontSelectedId; + + // Used by listview controls + THEME_COLUMN *ptcColumns; + DWORD cColumns; + + // Used by radio button controls + BOOL fLastRadioButton; + LPWSTR sczValue; + LPWSTR sczVariable; + + // Used by tab controls + THEME_TAB *pttTabs; + DWORD cTabs; + + // Used by controls that have text + DWORD cConditionalText; + THEME_CONDITIONAL_TEXT* rgConditionalText; + + // Used by command link controls + DWORD cConditionalNotes; + THEME_CONDITIONAL_TEXT* rgConditionalNotes; + + // state variables that should be ignored + HWND hWnd; + DWORD dwData; // type specific data +}; + + +struct THEME_IMAGELIST +{ + LPWSTR sczName; + + HIMAGELIST hImageList; +}; + +struct THEME_SAVEDVARIABLE +{ + LPWSTR wzName; + LPWSTR sczValue; +}; + +struct THEME_PAGE +{ + WORD wId; + LPWSTR sczName; + + DWORD cControlIndices; + + DWORD cSavedVariables; + THEME_SAVEDVARIABLE* rgSavedVariables; +}; + +struct THEME_FONT_INSTANCE +{ + UINT nDpi; + HFONT hFont; +}; + +struct THEME_FONT +{ + LONG lfHeight; + LONG lfWeight; + BYTE lfUnderline; + BYTE lfQuality; + LPWSTR sczFaceName; + + COLORREF crForeground; + HBRUSH hForeground; + COLORREF crBackground; + HBRUSH hBackground; + + DWORD cFontInstances; + THEME_FONT_INSTANCE* rgFontInstances; +}; + + +struct THEME +{ + WORD wId; + + BOOL fAutoResize; + BOOL fForceResize; + + DWORD dwStyle; + DWORD dwFontId; + HANDLE hIcon; + LPWSTR sczCaption; + int nDefaultDpiHeight; + int nDefaultDpiMinimumHeight; + int nDefaultDpiWidth; + int nDefaultDpiMinimumWidth; + int nHeight; + int nMinimumHeight; + int nWidth; + int nMinimumWidth; + int nWindowHeight; + int nWindowWidth; + int nSourceX; + int nSourceY; + UINT uStringId; + + HBITMAP hImage; + + DWORD cFonts; + THEME_FONT* rgFonts; + + DWORD cPages; + THEME_PAGE* rgPages; + + DWORD cImageLists; + THEME_IMAGELIST* rgImageLists; + + DWORD cControls; + THEME_CONTROL* rgControls; + + // internal state variables -- do not use outside ThmUtil.cpp + HWND hwndParent; // parent for loaded controls + HWND hwndHover; // current hwnd hovered over + DWORD dwCurrentPageId; + HWND hwndTooltip; + + UINT nDpi; + + // callback functions + PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition; + PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString; + PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable; + PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable; + PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable; + PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable; + + LPVOID pvVariableContext; +}; + + +/******************************************************************** + ThemeInitialize - initialized theme management. + +*******************************************************************/ +HRESULT DAPI ThemeInitialize( + __in_opt HMODULE hModule + ); + +/******************************************************************** + ThemeUninitialize - uninitialize theme management. + +*******************************************************************/ +void DAPI ThemeUninitialize(); + +/******************************************************************** + ThemeLoadFromFile - loads a theme from a loose file. + + *******************************************************************/ +HRESULT DAPI ThemeLoadFromFile( + __in_z LPCWSTR wzThemeFile, + __out THEME** ppTheme + ); + +/******************************************************************** + ThemeLoadFromResource - loads a theme from a module's data resource. + + NOTE: The resource data must be UTF-8 encoded. +*******************************************************************/ +HRESULT DAPI ThemeLoadFromResource( + __in_opt HMODULE hModule, + __in_z LPCSTR szResource, + __out THEME** ppTheme + ); + +/******************************************************************** + ThemeFree - frees any memory associated with a theme. + +*******************************************************************/ +void DAPI ThemeFree( + __in THEME* pTheme + ); + +/******************************************************************** +ThemeRegisterVariableCallbacks - registers a context and callbacks + for working with variables. + +*******************************************************************/ +HRESULT DAPI ThemeRegisterVariableCallbacks( + __in THEME* pTheme, + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, + __in_opt LPVOID pvContext + ); + +/******************************************************************** + ThemeCreateParentWindow - creates a parent window for the theme. + +*******************************************************************/ +HRESULT DAPI ThemeCreateParentWindow( + __in THEME* pTheme, + __in DWORD dwExStyle, + __in LPCWSTR szClassName, + __in LPCWSTR szWindowName, + __in DWORD dwStyle, + __in int x, + __in int y, + __in_opt HWND hwndParent, + __in_opt HINSTANCE hInstance, + __in_opt LPVOID lpParam, + __in THEME_WINDOW_INITIAL_POSITION initialPosition, + __out_opt HWND* phWnd + ); + +/******************************************************************** + ThemeLoadControls - creates the windows for all the theme controls + using the window created in ThemeCreateParentWindow. + +*******************************************************************/ +HRESULT DAPI ThemeLoadControls( + __in THEME* pTheme, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ); + +/******************************************************************** + ThemeUnloadControls - resets all the theme control windows so the theme + controls can be reloaded. + +*******************************************************************/ +void DAPI ThemeUnloadControls( + __in THEME* pTheme + ); + +/******************************************************************** + ThemeLocalize - Localizes all of the strings in the theme. + +*******************************************************************/ +HRESULT DAPI ThemeLocalize( + __in THEME *pTheme, + __in const WIX_LOCALIZATION *pLocStringSet + ); + +HRESULT DAPI ThemeLoadStrings( + __in THEME* pTheme, + __in HMODULE hResModule + ); + +/******************************************************************** + ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file. + + *******************************************************************/ +HRESULT DAPI ThemeLoadRichEditFromFile( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCWSTR wzFileName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeLoadRichEditFromResource - Attach a richedit control to resource data. + + *******************************************************************/ +HRESULT DAPI ThemeLoadRichEditFromResource( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeLoadRichEditFromResourceToHWnd - Attach a richedit control (by + HWND) to resource data. + + *******************************************************************/ +HRESULT DAPI ThemeLoadRichEditFromResourceToHWnd( + __in HWND hWnd, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeHandleKeyboardMessage - will translate the message using the active + accelerator table. + +*******************************************************************/ +BOOL DAPI ThemeHandleKeyboardMessage( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in MSG* pMsg + ); + +/******************************************************************** + ThemeDefWindowProc - replacement for DefWindowProc() when using theme. + +*******************************************************************/ +LRESULT CALLBACK ThemeDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +/******************************************************************** + ThemeGetPageIds - gets the page ids for the theme via page names. + +*******************************************************************/ +void DAPI ThemeGetPageIds( + __in const THEME* pTheme, + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, + __inout_ecount(cGetPages) DWORD* rgdwPageIds, + __in DWORD cGetPages + ); + +/******************************************************************** + ThemeGetPage - gets a theme page by id. + + *******************************************************************/ +THEME_PAGE* DAPI ThemeGetPage( + __in const THEME* pTheme, + __in DWORD dwPage + ); + +/******************************************************************** + ThemeShowPage - shows or hides all of the controls in the page at one time. + + *******************************************************************/ +HRESULT DAPI ThemeShowPage( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow + ); + +/******************************************************************** +ThemeShowPageEx - shows or hides all of the controls in the page at one time. + When using variables, TSPR_CANCEL reverts any changes made. + TSPR_REFRESH forces reevaluation of conditions. + It is expected that the current page is hidden before + showing a new page. + +*******************************************************************/ +HRESULT DAPI ThemeShowPageEx( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow, + __in THEME_SHOW_PAGE_REASON reason + ); + + +/******************************************************************** +ThemeShowChild - shows a control's specified child control, hiding the rest. + +*******************************************************************/ +void DAPI ThemeShowChild( + __in THEME* pTheme, + __in THEME_CONTROL* pParentControl, + __in DWORD dwIndex + ); + +/******************************************************************** + ThemeControlExists - check if a control with the specified id exists. + + *******************************************************************/ +BOOL DAPI ThemeControlExists( + __in const THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeControlEnable - enables/disables a control. + + *******************************************************************/ +void DAPI ThemeControlEnable( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fEnable + ); + +/******************************************************************** + ThemeControlEnabled - returns whether a control is enabled/disabled. + + *******************************************************************/ +BOOL DAPI ThemeControlEnabled( + __in THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeControlElevates - sets/removes the shield icon on a control. + + *******************************************************************/ +void DAPI ThemeControlElevates( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fElevates + ); + +/******************************************************************** + ThemeShowControl - shows/hides a control. + + *******************************************************************/ +void DAPI ThemeShowControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ); + +/******************************************************************** +ThemeShowControlEx - shows/hides a control with support for +conditional text and notes. + +*******************************************************************/ +void DAPI ThemeShowControlEx( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ); + +/******************************************************************** + ThemeControlVisible - returns whether a control is visible. + + *******************************************************************/ +BOOL DAPI ThemeControlVisible( + __in THEME* pTheme, + __in DWORD dwControl + ); + +BOOL DAPI ThemePostControlMessage( + __in THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +LRESULT DAPI ThemeSendControlMessage( + __in const THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +/******************************************************************** + ThemeDrawBackground - draws the theme background. + +*******************************************************************/ +HRESULT DAPI ThemeDrawBackground( + __in THEME* pTheme, + __in PAINTSTRUCT* pps + ); + +/******************************************************************** + ThemeDrawControl - draw an owner drawn control. + +*******************************************************************/ +HRESULT DAPI ThemeDrawControl( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis + ); + +/******************************************************************** + ThemeHoverControl - mark a control as hover. + +*******************************************************************/ +BOOL DAPI ThemeHoverControl( + __in THEME* pTheme, + __in HWND hwndParent, + __in HWND hwndControl + ); + +/******************************************************************** + ThemeIsControlChecked - gets whether a control is checked. Only + really useful for checkbox controls. + +*******************************************************************/ +BOOL DAPI ThemeIsControlChecked( + __in THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeSetControlColor - sets the color of text for a control. + +*******************************************************************/ +BOOL DAPI ThemeSetControlColor( + __in THEME* pTheme, + __in HDC hdc, + __in HWND hWnd, + __out HBRUSH* phBackgroundBrush + ); + +/******************************************************************** + ThemeSetProgressControl - sets the current percentage complete in a + progress bar control. + +*******************************************************************/ +HRESULT DAPI ThemeSetProgressControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwProgressPercentage + ); + +/******************************************************************** + ThemeSetProgressControlColor - sets the current color of a + progress bar control. + +*******************************************************************/ +HRESULT DAPI ThemeSetProgressControlColor( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwColorIndex + ); + +/******************************************************************** + ThemeSetTextControl - sets the text of a control. + +*******************************************************************/ +HRESULT DAPI ThemeSetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __in_z_opt LPCWSTR wzText + ); + +/******************************************************************** +ThemeSetTextControl - sets the text of a control and optionally + invalidates the control. + +*******************************************************************/ +HRESULT DAPI ThemeSetTextControlEx( + __in const THEME* pTheme, + __in DWORD dwControl, + __in BOOL fUpdate, + __in_z_opt LPCWSTR wzText + ); + +/******************************************************************** + ThemeGetTextControl - gets the text of a control. + +*******************************************************************/ +HRESULT DAPI ThemeGetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __inout_z LPWSTR* psczText + ); + +/******************************************************************** + ThemeUpdateCaption - updates the caption in the theme. + +*******************************************************************/ +HRESULT DAPI ThemeUpdateCaption( + __in THEME* pTheme, + __in_z LPCWSTR wzCaption + ); + +/******************************************************************** + ThemeSetFocus - set the focus to the control supplied or the next + enabled control if it is disabled. + +*******************************************************************/ +void DAPI ThemeSetFocus( + __in THEME* pTheme, + __in DWORD dwControl + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI TimeFromString( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ); +HRESULT DAPI TimeFromString3339( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ); +HRESULT DAPI TimeCurrentTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ); +HRESULT DAPI TimeCurrentDateTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ); +HRESULT DAPI TimeSystemDateTime( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in BOOL fGMT + ); +HRESULT DAPI TimeSystemToDateTimeString( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in LCID locale + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************* + UncConvertFromMountedDrive - Converts the string in-place from a + mounted drive path to a UNC path +*******************************************************************/ +DAPI_(HRESULT) UncConvertFromMountedDrive( + __inout LPWSTR *psczUNCPath, + __in LPCWSTR sczMountedDrivePath + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include "wininet.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum URI_PROTOCOL +{ + URI_PROTOCOL_UNKNOWN, + URI_PROTOCOL_FILE, + URI_PROTOCOL_FTP, + URI_PROTOCOL_HTTP, + URI_PROTOCOL_HTTPS, + URI_PROTOCOL_LOCAL, + URI_PROTOCOL_UNC +} URI_PROTOCOL; + +typedef struct _URI_INFO +{ + INTERNET_SCHEME scheme; + LPWSTR sczHostName; + INTERNET_PORT port; + LPWSTR sczUser; + LPWSTR sczPassword; + LPWSTR sczPath; + LPWSTR sczQueryString; +} URI_INFO; + + +HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ); + +HRESULT DAPI UriCrack( + __in_z LPCWSTR wzUri, + __out_opt INTERNET_SCHEME* pScheme, + __deref_opt_out_z LPWSTR* psczHostName, + __out_opt INTERNET_PORT* pPort, + __deref_opt_out_z LPWSTR* psczUser, + __deref_opt_out_z LPWSTR* psczPassword, + __deref_opt_out_z LPWSTR* psczPath, + __deref_opt_out_z LPWSTR* psczQueryString + ); + +HRESULT DAPI UriCrackEx( + __in_z LPCWSTR wzUri, + __in URI_INFO* pUriInfo + ); + +void DAPI UriInfoUninitialize( + __in URI_INFO* pUriInfo + ); + +HRESULT DAPI UriCreate( + __inout_z LPWSTR* psczUri, + __in INTERNET_SCHEME scheme, + __in_z_opt LPWSTR wzHostName, + __in INTERNET_PORT port, + __in_z_opt LPWSTR wzUser, + __in_z_opt LPWSTR wzPassword, + __in_z_opt LPWSTR wzPath, + __in_z_opt LPWSTR wzQueryString + ); + +HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ); + +HRESULT DAPI UriFile( + __deref_out_z LPWSTR* psczFile, + __in_z LPCWSTR wzUri + ); + +HRESULT DAPI UriProtocol( + __in_z LPCWSTR wzUri, + __out URI_PROTOCOL* pProtocol + ); + +HRESULT DAPI UriRoot( + __in_z LPCWSTR wzUri, + __out LPWSTR* ppwzRoot, + __out_opt URI_PROTOCOL* pProtocol + ); + +HRESULT DAPI UriResolve( + __in_z LPCWSTR wzUri, + __in_opt LPCWSTR wzBaseUri, + __out LPWSTR* ppwzResolvedUri, + __out_opt URI_PROTOCOL* pResolvedProtocol + ); + +#ifdef __cplusplus +} +#endif + 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI UserBuildDomainUserName( + __out_ecount_z(cchDest) LPWSTR wzDest, + __in int cchDest, + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain + ); + +HRESULT DAPI UserCheckIsMember( + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain, + __in_z LPCWSTR pwzGroupName, + __in_z LPCWSTR pwzGroupDomain, + __out LPBOOL lpfMember + ); + +HRESULT DAPI UserCreateADsPath( + __in_z LPCWSTR wzObjectDomain, + __in_z LPCWSTR wzObjectName, + __out BSTR *pbstrAdsPath + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; } + +typedef struct _VERUTIL_VERSION_RELEASE_LABEL +{ + BOOL fNumeric; + DWORD dwValue; + SIZE_T cchLabelOffset; + int cchLabel; +} VERUTIL_VERSION_RELEASE_LABEL; + +typedef struct _VERUTIL_VERSION +{ + LPWSTR sczVersion; + DWORD dwMajor; + DWORD dwMinor; + DWORD dwPatch; + DWORD dwRevision; + DWORD cReleaseLabels; + VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels; + SIZE_T cchMetadataOffset; + BOOL fInvalid; +} VERUTIL_VERSION; + +/******************************************************************* + VerCompareParsedVersions - compares the Verutil versions. + +*******************************************************************/ +HRESULT DAPI VerCompareParsedVersions( + __in_opt VERUTIL_VERSION* pVersion1, + __in_opt VERUTIL_VERSION* pVersion2, + __out int* pnResult + ); + +/******************************************************************* + VerCompareStringVersions - parses the strings with VerParseVersion and then + compares the Verutil versions with VerCompareParsedVersions. + +*******************************************************************/ +HRESULT DAPI VerCompareStringVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __in BOOL fStrict, + __out int* pnResult + ); + +/******************************************************************** + VerCopyVersion - copies the given Verutil version. + +*******************************************************************/ +HRESULT DAPI VerCopyVersion( + __in VERUTIL_VERSION* pSource, + __out VERUTIL_VERSION** ppVersion + ); + +/******************************************************************** + VerFreeVersion - frees any memory associated with a Verutil version. + +*******************************************************************/ +void DAPI VerFreeVersion( + __in VERUTIL_VERSION* pVersion + ); + +/******************************************************************* + VerParseVersion - parses the string into a Verutil version. + +*******************************************************************/ +HRESULT DAPI VerParseVersion( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __in BOOL fStrict, + __out VERUTIL_VERSION** ppVersion + ); + +/******************************************************************* + VerParseVersion - parses the QWORD into a Verutil version. + +*******************************************************************/ +HRESULT DAPI VerVersionFromQword( + __in DWORD64 qwVersion, + __out VERUTIL_VERSION** ppVersion + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// constants + +#define IDNOACTION 0 +#define WIU_MB_OKIGNORECANCELRETRY 0xE + +#define MAX_DARWIN_KEY 73 +#define MAX_DARWIN_COLUMN 255 + +#define WIU_LOG_DEFAULT INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | \ + INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_RESOLVESOURCE | \ + INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | \ + INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROPERTYDUMP + +#define ReleaseMsi(h) if (h) { ::MsiCloseHandle(h); } +#define ReleaseNullMsi(h) if (h) { ::MsiCloseHandle(h); h = NULL; } + + +typedef enum WIU_RESTART +{ + WIU_RESTART_NONE, + WIU_RESTART_REQUIRED, + WIU_RESTART_INITIATED, +} WIU_RESTART; + +typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE +{ + WIU_MSI_EXECUTE_MESSAGE_NONE, + WIU_MSI_EXECUTE_MESSAGE_PROGRESS, + WIU_MSI_EXECUTE_MESSAGE_ERROR, + WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE, + WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE, +} WIU_MSI_EXECUTE_MESSAGE_TYPE; + + +// structures + +typedef struct _WIU_MSI_EXECUTE_MESSAGE +{ + WIU_MSI_EXECUTE_MESSAGE_TYPE type; + DWORD dwAllowedResults; + + DWORD cData; + LPCWSTR* rgwzData; + + INT nResultRecommendation; // recommended return result for this message based on analysis of real world installs. + + union + { + struct + { + DWORD dwPercentage; + } progress; + struct + { + DWORD dwErrorCode; + LPCWSTR wzMessage; + } error; + struct + { + INSTALLMESSAGE mt; + LPCWSTR wzMessage; + } msiMessage; + struct + { + DWORD cFiles; + LPCWSTR* rgwzFiles; + } msiFilesInUse; + }; +} WIU_MSI_EXECUTE_MESSAGE; + +typedef struct _WIU_MSI_PROGRESS +{ + DWORD dwTotal; + DWORD dwCompleted; + DWORD dwStep; + BOOL fMoveForward; + BOOL fEnableActionData; + BOOL fScriptInProgress; +} WIU_MSI_PROGRESS; + + +typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ); + +typedef struct _WIU_MSI_EXECUTE_CONTEXT +{ + BOOL fRollback; + PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; + LPVOID pvContext; + WIU_MSI_PROGRESS rgMsiProgress[64]; + DWORD dwCurrentProgressIndex; + + INSTALLUILEVEL previousInstallUILevel; + HWND hwndPreviousParentWindow; + INSTALLUI_HANDLERW pfnPreviousExternalUI; + INSTALLUI_HANDLER_RECORD pfnPreviousExternalUIRecord; + + BOOL fSetPreviousExternalUIRecord; + BOOL fSetPreviousExternalUI; +} WIU_MSI_EXECUTE_CONTEXT; + + +// typedefs +typedef UINT (WINAPI *PFN_MSIENABLELOGW)( + __in DWORD dwLogMode, + __in_z LPCWSTR szLogFile, + __in DWORD dwLogAttributes + ); +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout LPDWORD pcchValue + ); +typedef INSTALLSTATE (WINAPI *PFN_MSIGETCOMPONENTPATHW)( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOEXW)( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); +typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)( + __in LPCWSTR szProduct, + __in LPCWSTR szFeature + ); +typedef UINT (WINAPI *PFN_MSIGETPATCHINFOEXW)( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out_opt LPWSTR wzValue, + __inout DWORD* pcchValue + ); +typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in DWORD cPatchInfo, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo + ); +typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)( + __in_z LPCWSTR wzProductPackagePath, + __in DWORD cPatchInfo, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo + ); +typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)( + __in LPCWSTR szPackagePath, + __in_opt LPCWSTR szCommandLine + ); +typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)( + __in LPCWSTR szProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_opt LPCWSTR szCommandLine + ); +typedef UINT (WINAPI *PFN_MSIREMOVEPATCHESW)( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in INSTALLTYPE eUninstallType, + __in_z_opt LPCWSTR szPropertyList + ); +typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)( + __in INSTALLUILEVEL dwUILevel, + __inout_opt HWND *phWnd + ); +typedef UINT (WINAPI *PFN_MSISETEXTERNALUIRECORD)( + __in_opt INSTALLUI_HANDLER_RECORD puiHandler, + __in DWORD dwMessageFilter, + __in_opt LPVOID pvContext, + __out_opt PINSTALLUI_HANDLER_RECORD ppuiPrevHandler + ); +typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)( + __in_opt INSTALLUI_HANDLERW puiHandler, + __in DWORD dwMessageFilter, + __in_opt LPVOID pvContext + ); +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf + ); +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSEXW)( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ); + +typedef UINT (WINAPI *PFN_MSIENUMRELATEDPRODUCTSW)( + __in LPCWSTR lpUpgradeCode, + __reserved DWORD dwReserved, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf + ); +typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)( + __in LPCWSTR szProductCodeOrPatchCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwOptions, + __in LPCWSTR szSource, + __in_opt DWORD dwIndex + ); +typedef UINT(WINAPI* PFN_MSIBEGINTRANSACTIONW)( + __in LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE* phTransactionHandle, + __out HANDLE* phChangeOfOwnerEvent + ); +typedef UINT(WINAPI* PFN_MSIENDTRANSACTION)( + __in DWORD dwTransactionState + ); + + +HRESULT DAPI WiuInitialize( + ); +void DAPI WiuUninitialize( + ); +void DAPI WiuFunctionOverride( + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW + ); +HRESULT DAPI WiuGetComponentPath( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI WiuLocateComponent( + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI WiuQueryFeatureState( + __in_z LPCWSTR wzProduct, + __in_z LPCWSTR wzFeature, + __out INSTALLSTATE* pInstallState + ); +HRESULT DAPI WiuGetProductInfo( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetProductInfoEx( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetProductProperty( + __in MSIHANDLE hProduct, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetPatchInfoEx( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuDeterminePatchSequence( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ); +HRESULT DAPI WiuDetermineApplicablePatches( + __in_z LPCWSTR wzProductPackagePath, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ); +HRESULT DAPI WiuEnumProducts( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ); +HRESULT DAPI WiuEnumProductsEx( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ); +HRESULT DAPI WiuEnumRelatedProducts( + __in_z LPCWSTR wzUpgradeCode, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ); +HRESULT DAPI WiuEnumRelatedProductCodes( + __in_z LPCWSTR wzUpgradeCode, + __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, + __out DWORD* pcRelatedProducts, + __in BOOL fReturnHighestVersionOnly + ); +HRESULT DAPI WiuEnableLog( + __in DWORD dwLogMode, + __in_z LPCWSTR wzLogFile, + __in DWORD dwLogAttributes + ); +HRESULT DAPI WiuInitializeInternalUI( + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +HRESULT DAPI WiuInitializeExternalUI( + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in LPVOID pvContext, + __in BOOL fRollback, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +void DAPI WiuUninitializeExternalUI( + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +HRESULT DAPI WiuConfigureProductEx( + __in_z LPCWSTR wzProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuInstallProduct( + __in_z LPCWSTR wzPackagPath, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuRemovePatches( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzPropertyList, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuSourceListAddSourceEx( + __in_z LPCWSTR wzProductCodeOrPatchCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwCode, + __in_z LPCWSTR wzSource, + __in_opt DWORD dwIndex + ); +HRESULT DAPI WiuBeginTransaction( + __in_z LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE* phTransactionHandle, + __out HANDLE* phChangeOfOwnerEvent, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ); +HRESULT DAPI WiuEndTransaction( + __in DWORD dwTransactionState, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ); +BOOL DAPI WiuIsMsiTransactionSupported( + ); + +#ifdef __cplusplus +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT DAPI WuaPauseAutomaticUpdates(); + +HRESULT DAPI WuaResumeAutomaticUpdates(); + +HRESULT DAPI WuaRestartRequired( + __out BOOL* pfRestartRequired + ); + +#if defined(__cplusplus) +} +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; + +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}}; + +typedef enum XML_LOAD_ATTRIBUTE +{ + XML_LOAD_PRESERVE_WHITESPACE = 1, +} XML_LOAD_ATTRIBUTE; + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI XmlInitialize(); +void DAPI XmlUninitialize(); + +HRESULT DAPI XmlCreateElement( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzElementName, + __out IXMLDOMElement **ppixnElement + ); +HRESULT DAPI XmlCreateDocument( + __in_opt LPCWSTR pwzElementName, + __out IXMLDOMDocument** ppixdDocument, + __out_opt IXMLDOMElement** ppixeRootElement = NULL + ); +HRESULT DAPI XmlLoadDocument( + __in_z LPCWSTR wzDocument, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentEx( + __in_z LPCWSTR wzDocument, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromFile( + __in_z LPCWSTR wzPath, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromBuffer( + __in_bcount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromFileEx( + __in_z LPCWSTR wzPath, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlSelectSingleNode( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNode **ppixnChild + ); +HRESULT DAPI XmlSetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in_z LPCWSTR pwzAttributeValue + ); +HRESULT DAPI XmlCreateTextNode( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzText, + __out IXMLDOMText **ppixnTextNode + ); +HRESULT DAPI XmlGetText( + __in IXMLDOMNode* pixnNode, + __deref_out_z BSTR* pbstrText + ); +HRESULT DAPI XmlGetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __deref_out_z BSTR* pbstrAttributeValue + ); +HRESULT DAPI XmlGetAttributeEx( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __deref_out_z LPWSTR* psczAttributeValue + ); +HRESULT DAPI XmlGetYesNoAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __out BOOL* pfYes + ); +HRESULT DAPI XmlGetAttributeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD* pdwValue + ); +HRESULT DAPI XmlGetAttributeNumberBase( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in int nBase, + __out DWORD* pdwValue + ); +HRESULT DAPI XmlGetAttributeLargeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD64* pdw64Value + ); +HRESULT DAPI XmlGetNamedItem( + __in IXMLDOMNamedNodeMap *pixnmAttributes, + __in_opt LPCWSTR wzName, + __out IXMLDOMNode **ppixnNamedItem + ); +HRESULT DAPI XmlSetText( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzText + ); +HRESULT DAPI XmlSetTextNumber( + __in IXMLDOMNode *pixnNode, + __in DWORD dwValue + ); +HRESULT DAPI XmlCreateChild( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR pwzElementType, + __out IXMLDOMNode** ppixnChild + ); +HRESULT DAPI XmlRemoveAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute + ); +HRESULT DAPI XmlSelectNodes( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNodeList **ppixnChild + ); +HRESULT DAPI XmlNextAttribute( + __in IXMLDOMNamedNodeMap* pixnnm, + __out IXMLDOMNode** pixnAttribute, + __deref_opt_out_z_opt BSTR* pbstrAttribute + ); +HRESULT DAPI XmlNextElement( + __in IXMLDOMNodeList* pixnl, + __out IXMLDOMNode** pixnElement, + __deref_opt_out_z_opt BSTR* pbstrElement + ); +HRESULT DAPI XmlRemoveChildren( + __in IXMLDOMNode* pixnSource, + __in_z LPCWSTR pwzXPath + ); +HRESULT DAPI XmlSaveDocument( + __in IXMLDOMDocument* pixdDocument, + __inout LPCWSTR wzPath + ); +HRESULT DAPI XmlSaveDocumentToBuffer( + __in IXMLDOMDocument* pixdDocument, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out DWORD* pcbDest + ); + +#ifdef __cplusplus +} +#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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define InetExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) +#define InetExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) +#define InetExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) +#define InetExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) +#define InetExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INETUTIL, e, x, s, __VA_ARGS__) +#define InetExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INETUTIL, g, x, s, __VA_ARGS__) + + +/******************************************************************* + InternetGetSizeByHandle - returns size of file by url handle + +*******************************************************************/ +extern "C" HRESULT DAPI InternetGetSizeByHandle( + __in HINTERNET hiFile, + __out LONGLONG* pllSize + ) +{ + Assert(pllSize); + + HRESULT hr = S_OK; + DWORD dwSize = 0; + DWORD cb = 0; + + cb = sizeof(dwSize); + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast(&dwSize), &cb, NULL)) + { + InetExitOnLastError(hr, "Failed to get size for internet file handle"); + } + + *pllSize = dwSize; +LExit: + return hr; +} + + +/******************************************************************* + InetGetCreateTimeByHandle - returns url creation time + +******************************************************************/ +extern "C" HRESULT DAPI InternetGetCreateTimeByHandle( + __in HINTERNET hiFile, + __out LPFILETIME pft + ) +{ + Assert(pft); + + HRESULT hr = S_OK; + SYSTEMTIME st = {0 }; + DWORD cb = sizeof(SYSTEMTIME); + + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast(&st), &cb, NULL)) + { + InetExitWithLastError(hr, "failed to get create time for internet file handle"); + } + + if (!::SystemTimeToFileTime(&st, pft)) + { + InetExitWithLastError(hr, "failed to convert system time to file time"); + } + +LExit: + return hr; +} + + +/******************************************************************* + InternetQueryInfoString - query info string + +*******************************************************************/ +extern "C" HRESULT DAPI InternetQueryInfoString( + __in HINTERNET hRequest, + __in DWORD dwInfo, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + SIZE_T cbOriginal = 0; + DWORD cbValue = 0; + DWORD dwIndex = 0; + + // If nothing was provided start off with some arbitrary size. + if (!*psczValue) + { + hr = StrAlloc(psczValue, 64); + InetExitOnFailure(hr, "Failed to allocate memory for value."); + } + + hr = StrSize(*psczValue, &cbOriginal); + InetExitOnFailure(hr, "Failed to get size of value."); + + cbValue = (DWORD)min(DWORD_MAX, cbOriginal); + + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) + { + DWORD er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + cbValue += sizeof(WCHAR); // add one character for the null terminator. + + hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR)); + InetExitOnFailure(hr, "Failed to allocate value."); + + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) + { + er = ::GetLastError(); + } + else + { + er = ERROR_SUCCESS; + } + } + + hr = HRESULT_FROM_WIN32(er); + InetExitOnRootFailure(hr, "Failed to get query information."); + } + +LExit: + return hr; +} + + +/******************************************************************* + InternetQueryInfoNumber - query info number + +*******************************************************************/ +extern "C" HRESULT DAPI InternetQueryInfoNumber( + __in HINTERNET hRequest, + __in DWORD dwInfo, + __inout LONG* plInfo + ) +{ + HRESULT hr = S_OK; + DWORD cbCode = sizeof(LONG); + DWORD dwIndex = 0; + + if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast(plInfo), &cbCode, &dwIndex)) + { + InetExitWithLastError(hr, "Failed to get query information."); + } + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__) +#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__) + +const LPCWSTR wzSectionSeparator = L"\\"; + +struct INI_STRUCT +{ + LPWSTR sczPath; // the path to the INI file to be parsed + + LPWSTR sczOpenTagPrefix; // For regular ini, this would be '[' + LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']' + + LPWSTR sczValuePrefix; // for regular ini, this would be NULL + LPWSTR sczValueSeparator; // for regular ini, this would be '=' + + LPWSTR *rgsczValueSeparatorExceptions; + DWORD cValueSeparatorExceptions; + + LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';' + + INI_VALUE *rgivValues; + DWORD cValues; + + LPWSTR *rgsczLines; + DWORD cLines; + + FILE_ENCODING feEncoding; + BOOL fModified; +}; + +const int INI_HANDLE_BYTES = sizeof(INI_STRUCT); + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_inout_z LPWSTR* psczOutput + ); +static void UninitializeIniValue( + INI_VALUE *pivValue + ); + +extern "C" HRESULT DAPI IniInitialize( + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle + ) +{ + HRESULT hr = S_OK; + + // Allocate the handle + *piHandle = static_cast(MemAlloc(sizeof(INI_STRUCT), TRUE)); + IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); + +LExit: + return hr; +} + +extern "C" void DAPI IniUninitialize( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle + ) +{ + INI_STRUCT *pi = static_cast(piHandle); + + ReleaseStr(pi->sczPath); + ReleaseStr(pi->sczOpenTagPrefix); + ReleaseStr(pi->sczOpenTagPostfix); + ReleaseStr(pi->sczValuePrefix); + ReleaseStr(pi->sczValueSeparator); + + for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i) + { + ReleaseStr(pi->rgsczValueSeparatorExceptions + i); + } + + ReleaseStr(pi->sczCommentLinePrefix); + + for (DWORD i = 0; i < pi->cValues; ++i) + { + UninitializeIniValue(pi->rgivValues + i); + } + ReleaseMem(pi->rgivValues); + + ReleaseStrArray(pi->rgsczLines, pi->cLines); + + ReleaseMem(pi); +} + +extern "C" HRESULT DAPI IniSetOpenTag( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzOpenTagPrefix, + __in_z_opt LPCWSTR wzOpenTagPostfix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzOpenTagPrefix) + { + hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0); + IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + + if (wzOpenTagPostfix) + { + hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0); + IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzValuePrefix, + __in_z_opt LPCWSTR wzValueSeparator + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzValuePrefix) + { + hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0); + IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); + } + else + { + ReleaseNullStr(pi->sczValuePrefix); + } + + if (wzValueSeparator) + { + hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0); + IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); + } + else + { + ReleaseNullStr(pi->sczValueSeparator); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueSeparatorException( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z LPCWSTR wzValueNamePrefix + ) +{ + HRESULT hr = S_OK; + DWORD dwInsertedIndex = 0; + + INI_STRUCT *pi = static_cast(piHandle); + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5); + IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); + dwInsertedIndex = pi->cValueSeparatorExceptions; + ++pi->cValueSeparatorExceptions; + + hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0); + IniExitOnFailure(hr, "Failed to copy value separator exception"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetCommentStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzLinePrefix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzLinePrefix) + { + hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0); + IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); + } + else + { + ReleaseNullStr(pi->sczCommentLinePrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniParse( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzPath, + __out_opt FILE_ENCODING *pfeEncodingFound + ) +{ + HRESULT hr = S_OK; + DWORD dwValuePrefixLength = 0; + DWORD dwValueSeparatorExceptionLength = 0; + LPWSTR sczContents = NULL; + LPWSTR sczCurrentSection = NULL; + LPWSTR sczName = NULL; + LPWSTR sczNameTrimmed = NULL; + LPWSTR sczValue = NULL; + LPWSTR sczValueTrimmed = NULL; + LPWSTR wzOpenTagPrefix = NULL; + LPWSTR wzOpenTagPostfix = NULL; + LPWSTR wzValuePrefix = NULL; + LPWSTR wzValueNameStart = NULL; + LPWSTR wzValueSeparator = NULL; + LPWSTR wzCommentLinePrefix = NULL; + LPWSTR wzValueBegin = NULL; + LPCWSTR wzTemp = NULL; + + INI_STRUCT *pi = static_cast(piHandle); + + BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix); + BOOL fValuePrefix = (NULL != pi->sczValuePrefix); + + hr = StrAllocString(&pi->sczPath, wzPath, 0); + IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); + + hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding); + IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); + + if (pfeEncodingFound) + { + *pfeEncodingFound = pi->feEncoding; + } + + if (!sczContents || !*sczContents) + { + // Empty string, nothing to parse + ExitFunction1(hr = S_OK); + } + + dwValuePrefixLength = lstrlenW(pi->sczValuePrefix); + hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast(&pi->cLines), sczContents, L"\n"); + IniExitOnFailure(hr, "Failed to split INI file into lines"); + + for (DWORD i = 0; i < pi->cLines; ++i) + { + if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i]) + { + continue; + } + + if (pi->sczCommentLinePrefix) + { + wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix); + + if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1) + { + continue; + } + } + + if (pi->sczOpenTagPrefix) + { + wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix); + if (wzOpenTagPrefix) + { + // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix + // This is important, for example, to support values with names like "Array[0]=blah" in INI format + for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp) + { + if (*wzTemp != L' ' && *wzTemp != L'\t') + { + wzOpenTagPrefix = NULL; + break; + } + } + } + } + + if (pi->sczOpenTagPostfix) + { + wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix); + } + + if (pi->sczValuePrefix) + { + wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix); + if (wzValuePrefix != NULL) + { + wzValueNameStart = wzValuePrefix + dwValuePrefixLength; + } + } + else + { + wzValueNameStart = pi->rgsczLines[i]; + } + + if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0') + { + dwValueSeparatorExceptionLength = 0; + for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j) + { + if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j])) + { + dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]); + break; + } + } + + wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator); + } + + // Don't keep the endline + if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r') + { + pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0'; + } + + if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix)) + { + // There is an section starting here, let's keep track of it and move on + hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix))); + IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); + + // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix) + && (!fValuePrefix || wzValuePrefix)) + { + if (fValuePrefix) + { + wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix); + } + else + { + wzValueBegin = pi->rgsczLines[i]; + } + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100); + IniExitOnFailure(hr, "Failed to increase array size for value array"); + + if (sczCurrentSection) + { + hr = StrAllocString(&sczName, sczCurrentSection, 0); + IniExitOnFailure(hr, "Failed to copy current section name"); + + hr = StrAllocConcat(&sczName, wzSectionSeparator, 0); + IniExitOnFailure(hr, "Failed to copy current section name"); + } + + hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin); + IniExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0); + IniExitOnFailure(hr, "Failed to copy value"); + + hr = StrTrimWhitespace(&sczNameTrimmed, sczName); + IniExitOnFailure(hr, "Failed to trim whitespace from name"); + + hr = StrTrimWhitespace(&sczValueTrimmed, sczValue); + IniExitOnFailure(hr, "Failed to trim whitespace from value"); + + pi->rgivValues[pi->cValues].wzName = const_cast(sczNameTrimmed); + sczNameTrimmed = NULL; + pi->rgivValues[pi->cValues].wzValue = const_cast(sczValueTrimmed); + sczValueTrimmed = NULL; + pi->rgivValues[pi->cValues].dwLineNumber = i + 1; + + ++pi->cValues; + + // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else + { + // Must be a comment, so ignore it and keep it in the list to output + } + + ReleaseNullStr(sczName); + } + +LExit: + ReleaseStr(sczCurrentSection); + ReleaseStr(sczContents); + ReleaseStr(sczName); + ReleaseStr(sczNameTrimmed); + ReleaseStr(sczValue); + ReleaseStr(sczValueTrimmed); + + return hr; +} + +extern "C" HRESULT DAPI IniGetValueList( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, + __out DWORD *pcValues + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + *prgivValues = pi->rgivValues; + *pcValues = pi->cValues; + + return hr; +} + +extern "C" HRESULT DAPI IniGetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + if (NULL == pValue) + { + hr = E_NOTFOUND; + IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); + } + + if (NULL == pValue->wzValue) + { + ExitFunction1(hr = E_NOTFOUND); + } + + hr = StrAllocString(psczValue, pValue->wzValue, 0); + IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSectionPrefix = NULL; // includes section name and backslash + LPWSTR sczName = NULL; + LPWSTR sczValue = NULL; + DWORD dwInsertIndex = DWORD_MAX; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + // We're killing the value + if (NULL == wzValue) + { + if (pValue && pValue->wzValue) + { + pi->fModified = TRUE; + sczValue = const_cast(pValue->wzValue); + pValue->wzValue = NULL; + ReleaseNullStr(sczValue); + } + + ExitFunction(); + } + else + { + if (pValue) + { + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1)) + { + pi->fModified = TRUE; + hr = StrAllocString(const_cast(&pValue->wzValue), wzValue, 0); + IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); + } + + ExitFunction1(hr = S_OK); + } + else + { + if (wzValueName) + { + hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix); + IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); + } + + // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in) + if (sczSectionPrefix) + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix))) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + else + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator)) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + + // Otherwise, just add it to the end + if (DWORD_MAX == dwInsertIndex) + { + dwInsertIndex = pi->cValues; + } + + pi->fModified = TRUE; + hr = MemInsertIntoArray(reinterpret_cast(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100); + IniExitOnFailure(hr, "Failed to insert value into array"); + + hr = StrAllocString(&sczName, wzValueName, 0); + IniExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValue, 0); + IniExitOnFailure(hr, "Failed to copy value"); + + pi->rgivValues[dwInsertIndex].wzName = const_cast(sczName); + sczName = NULL; + pi->rgivValues[dwInsertIndex].wzValue = const_cast(sczValue); + sczValue = NULL; + + ++pi->cValues; + } + } + +LExit: + ReleaseStr(sczName); + ReleaseStr(sczValue); + + return hr; +} + +extern "C" HRESULT DAPI IniWriteFile( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzPath, + __in FILE_ENCODING feOverrideEncoding + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentSectionPrefix = NULL; + LPWSTR sczNewSectionPrefix = NULL; + LPWSTR sczContents = NULL; + LPCWSTR wzName = NULL; + DWORD dwLineArrayIndex = 1; + FILE_ENCODING feEncoding; + + INI_STRUCT *pi = static_cast(piHandle); + + if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding) + { + feEncoding = pi->feEncoding; + } + else + { + feEncoding = feOverrideEncoding; + } + + if (FILE_ENCODING_UNSPECIFIED == feEncoding) + { + feEncoding = FILE_ENCODING_UTF16_WITH_BOM; + } + + if (!pi->fModified) + { + ExitFunction1(hr = S_OK); + } + if (NULL == wzPath && NULL == pi->sczPath) + { + ExitFunction1(hr = E_NOTFOUND); + } + + BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix); + + hr = StrAllocString(&sczContents, L"", 0); + IniExitOnFailure(hr, "Failed to begin contents string as empty string"); + + // Insert any beginning lines we didn't understand like comments + if (0 < pi->cLines) + { + while (pi->rgsczLines[dwLineArrayIndex]) + { + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ++dwLineArrayIndex; + } + } + + for (DWORD i = 0; i < pi->cValues; ++i) + { + // Skip if this value was killed off + if (NULL == pi->rgivValues[i].wzValue) + { + continue; + } + + // Now generate any lines for the current value like value line and maybe also a new section line before it + + // First see if we need to write a section line + hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix); + IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); + + // If the new section prefix is different, write a section out for it + if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1))) + { + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0); + IniExitOnFailure(hr, "Failed to concat open tag prefix to string"); + + // Exclude section separator (i.e. backslash) from new section prefix + hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator)); + IniExitOnFailure(hr, "Failed to concat section name to string"); + + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0); + IniExitOnFailure(hr, "Failed to concat open tag postfix to string"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ReleaseNullStr(sczCurrentSectionPrefix); + sczCurrentSectionPrefix = sczNewSectionPrefix; + sczNewSectionPrefix = NULL; + } + + // Inserting lines we read before the current value if appropriate + while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex) + { + // Skip any lines were purposely forgot + if (NULL == pi->rgsczLines[dwLineArrayIndex]) + { + ++dwLineArrayIndex; + continue; + } + + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + wzName = pi->rgivValues[i].wzName; + if (fSections) + { + wzName += lstrlenW(sczCurrentSectionPrefix); + } + + // OK, now just write the name/value pair, if it isn't deleted + if (pi->sczValuePrefix) + { + hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0); + IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); + } + + hr = StrAllocConcat(&sczContents, wzName, 0); + IniExitOnFailure(hr, "Failed to concat value name to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0); + IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0); + IniExitOnFailure(hr, "Failed to concat value to ini output buffer"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + // If no path was specified, use the path to the file we parsed + if (NULL == wzPath) + { + wzPath = pi->sczPath; + } + + hr = FileFromString(wzPath, 0, sczContents, feEncoding); + IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); + +LExit: + ReleaseStr(sczContents); + ReleaseStr(sczCurrentSectionPrefix); + ReleaseStr(sczNewSectionPrefix); + + return hr; +} + +static void UninitializeIniValue( + INI_VALUE *pivValue + ) +{ + ReleaseStr(const_cast(pivValue->wzName)); + ReleaseStr(const_cast(pivValue->wzValue)); +} + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_inout_z LPWSTR* psczOutput + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzSectionDelimiter = NULL; + + ReleaseNullStr(*psczOutput); + + wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator); + if (wzSectionDelimiter && wzSectionDelimiter != wzName) + { + hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1); + IniExitOnFailure(hr, "Failed to copy section prefix"); + } + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define JsonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) +#define JsonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) +#define JsonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) +#define JsonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) +#define JsonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_JSONUTIL, e, x, s, __VA_ARGS__) +#define JsonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_JSONUTIL, g, x, s, __VA_ARGS__) + +const DWORD JSON_STACK_INCREMENT = 5; + +// Prototypes +static HRESULT DoStart( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenStart, + __in_z LPCWSTR wzStartString + ); +static HRESULT DoEnd( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenEnd, + __in_z LPCWSTR wzEndString + ); +static HRESULT DoKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ); +static HRESULT DoValue( + __in JSON_WRITER* pWriter, + __in_z_opt LPCWSTR wzValue + ); +static HRESULT EnsureTokenStack( + __in JSON_WRITER* pWriter + ); +static HRESULT SerializeJsonString( + __out_z LPWSTR* psczJsonString, + __in_z LPCWSTR wzString + ); + + + +DAPI_(HRESULT) JsonInitializeReader( + __in_z LPCWSTR wzJson, + __in JSON_READER* pReader + ) +{ + HRESULT hr = S_OK; + + memset(pReader, 0, sizeof(JSON_READER)); + ::InitializeCriticalSection(&pReader->cs); + + hr = StrAllocString(&pReader->sczJson, wzJson, 0); + JsonExitOnFailure(hr, "Failed to allocate json string."); + + pReader->pwz = pReader->sczJson; + +LExit: + return hr; +} + + +DAPI_(void) JsonUninitializeReader( + __in JSON_READER* pReader + ) +{ + ReleaseStr(pReader->sczJson); + + ::DeleteCriticalSection(&pReader->cs); + memset(pReader, 0, sizeof(JSON_READER)); +} + + +static HRESULT NextToken( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken + ) +{ + HRESULT hr = S_OK; + + // Skip whitespace. + while (L' ' == *pReader->pwz || + L'\t' == *pReader->pwz || + L'\r' == *pReader->pwz || + L'\n' == *pReader->pwz || + L',' == *pReader->pwz) + { + ++pReader->pwz; + } + + switch (*pReader->pwz) + { + case L'{': + *pToken = JSON_TOKEN_OBJECT_START; + break; + + case L':': // begin object value + *pToken = JSON_TOKEN_OBJECT_VALUE; + break; + + case L'}': // end object + *pToken = JSON_TOKEN_OBJECT_END; + break; + + case L'[': // begin array + *pToken = JSON_TOKEN_ARRAY_START; + break; + + case L']': // end array + *pToken = JSON_TOKEN_ARRAY_END; + break; + + case L'"': // string + *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY; + break; + + case L't': // true + case L'f': // false + case L'n': // null + case L'-': // number + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + *pToken = JSON_TOKEN_VALUE; + break; + + case L'\0': + hr = E_NOMOREITEMS; + break; + + default: + hr = E_INVALIDSTATE; + } + + return hr; +} + + +DAPI_(HRESULT) JsonReadNext( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken, + __out JSON_VALUE* pValue + ) +{ + HRESULT hr = S_OK; + //WCHAR wz; + //JSON_TOKEN token = JSON_TOKEN_NONE; + + ::EnterCriticalSection(&pReader->cs); + + hr = NextToken(pReader, pToken); + if (E_NOMOREITEMS == hr) + { + ExitFunction(); + } + JsonExitOnFailure(hr, "Failed to get next token."); + + if (JSON_TOKEN_VALUE == *pToken) + { + hr = JsonReadValue(pReader, pValue); + } + else + { + ++pReader->pwz; + } + +LExit: + ::LeaveCriticalSection(&pReader->cs); + return hr; +} + + +DAPI_(HRESULT) JsonReadValue( + __in JSON_READER* /*pReader*/, + __in JSON_VALUE* /*pValue*/ + ) +{ + HRESULT hr = S_OK; + +//LExit: + return hr; +} + + +DAPI_(HRESULT) JsonInitializeWriter( + __in JSON_WRITER* pWriter + ) +{ + memset(pWriter, 0, sizeof(JSON_WRITER)); + ::InitializeCriticalSection(&pWriter->cs); + + return S_OK; +} + + +DAPI_(void) JsonUninitializeWriter( + __in JSON_WRITER* pWriter + ) +{ + ReleaseMem(pWriter->rgTokenStack); + ReleaseStr(pWriter->sczJson); + + ::DeleteCriticalSection(&pWriter->cs); + memset(pWriter, 0, sizeof(JSON_WRITER)); +} + + +DAPI_(HRESULT) JsonWriteBool( + __in JSON_WRITER* pWriter, + __in BOOL fValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0); + JsonExitOnFailure(hr, "Failed to convert boolean to string."); + + hr = DoValue(pWriter, sczValue); + JsonExitOnFailure(hr, "Failed to add boolean to JSON."); + +LExit: + ReleaseStr(sczValue); + return hr; +} + + +DAPI_(HRESULT) JsonWriteNumber( + __in JSON_WRITER* pWriter, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = StrAllocFormatted(&sczValue, L"%u", dwValue); + JsonExitOnFailure(hr, "Failed to convert number to string."); + + hr = DoValue(pWriter, sczValue); + JsonExitOnFailure(hr, "Failed to add number to JSON."); + +LExit: + ReleaseStr(sczValue); + return hr; +} + + +DAPI_(HRESULT) JsonWriteString( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczJsonString = NULL; + + hr = SerializeJsonString(&sczJsonString, wzValue); + JsonExitOnFailure(hr, "Failed to allocate string JSON."); + + hr = DoValue(pWriter, sczJsonString); + JsonExitOnFailure(hr, "Failed to add string to JSON."); + +LExit: + ReleaseStr(sczJsonString); + return hr; +} + + +DAPI_(HRESULT) JsonWriteArrayStart( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"["); + JsonExitOnFailure(hr, "Failed to start JSON array."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteArrayEnd( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]"); + JsonExitOnFailure(hr, "Failed to end JSON array."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectStart( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{"); + JsonExitOnFailure(hr, "Failed to start JSON object."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczObjectKey = NULL; + + hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey); + JsonExitOnFailure(hr, "Failed to allocate JSON object key."); + + hr = DoKey(pWriter, sczObjectKey); + JsonExitOnFailure(hr, "Failed to add object key to JSON."); + +LExit: + ReleaseStr(sczObjectKey); + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectEnd( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}"); + JsonExitOnFailure(hr, "Failed to end JSON object."); + +LExit: + return hr; +} + + +static HRESULT DoStart( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenStart, + __in_z LPCWSTR wzStartString + ) +{ + Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart); + + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + BOOL fPushToken = TRUE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + JsonExitOnFailure(hr, "Failed to ensure token stack for start."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_NONE: + token = tokenStart; + fPushToken = FALSE; + break; + + case JSON_TOKEN_ARRAY_START: // array start changes to array value. + token = JSON_TOKEN_ARRAY_VALUE; + break; + + case JSON_TOKEN_ARRAY_VALUE: + case JSON_TOKEN_ARRAY_END: + case JSON_TOKEN_OBJECT_END: + fNeedComma = TRUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + JsonExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + JsonExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); + } + + hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); + JsonExitOnFailure(hr, "Failed to start JSON array or object."); + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + if (fPushToken) + { + pWriter->rgTokenStack[pWriter->cTokens] = tokenStart; + ++pWriter->cTokens; + } + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoEnd( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenEnd, + __in_z LPCWSTR wzEndString + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pWriter->cs); + + if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) + { + hr = E_UNEXPECTED; + JsonExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); + } + else + { + JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) || + (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) + { + hr = E_UNEXPECTED; + JsonExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); + } + } + + hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); + JsonExitOnFailure(hr, "Failed to end JSON array or object."); + + --pWriter->cTokens; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ) +{ + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + JsonExitOnFailure(hr, "Failed to ensure token stack for key."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_OBJECT_START: + token = JSON_TOKEN_OBJECT_KEY; + break; + + case JSON_TOKEN_OBJECT_VALUE: + token = JSON_TOKEN_OBJECT_KEY; + fNeedComma = TRUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + JsonExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + JsonExitOnFailure(hr, "Failed to add comma for key to JSON."); + } + + hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); + JsonExitOnFailure(hr, "Failed to add key to JSON."); + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoValue( + __in JSON_WRITER* pWriter, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + JsonExitOnFailure(hr, "Failed to ensure token stack for value."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_ARRAY_START: + token = JSON_TOKEN_ARRAY_VALUE; + break; + + case JSON_TOKEN_ARRAY_VALUE: + fNeedComma = TRUE; + break; + + case JSON_TOKEN_OBJECT_KEY: + token = JSON_TOKEN_OBJECT_VALUE; + break; + + case JSON_TOKEN_NONE: + token = JSON_TOKEN_VALUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + JsonExitOnRootFailure(hr, "Cannot add value to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + JsonExitOnFailure(hr, "Failed to add comma for value to JSON."); + } + + if (wzValue) + { + hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0); + JsonExitOnFailure(hr, "Failed to add value to JSON."); + } + else + { + hr = StrAllocConcat(&pWriter->sczJson, L"null", 0); + JsonExitOnFailure(hr, "Failed to add null value to JSON."); + } + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT EnsureTokenStack( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0; + + hr = MemEnsureArraySize(reinterpret_cast(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT); + JsonExitOnFailure(hr, "Failed to allocate JSON token stack."); + + if (0 == pWriter->cTokens) + { + pWriter->rgTokenStack[0] = JSON_TOKEN_NONE; + ++pWriter->cTokens; + } + +LExit: + return hr; +} + + +static HRESULT SerializeJsonString( + __out_z LPWSTR* psczJsonString, + __in_z LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0) + + for (LPCWSTR pch = wzString; *pch; ++pch) + { + // If it is a special JSON character, add space for the escape backslash. + if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch) + { + ++cchRequired; + } + + ++cchRequired; + } + + hr = StrAlloc(psczJsonString, cchRequired); + JsonExitOnFailure(hr, "Failed to allocate space for JSON string."); + + LPWSTR pchTarget = *psczJsonString; + + *pchTarget = L'\"'; + ++pchTarget; + + for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget) + { + // If it is a special JSON character, handle it or just add the character as is. + switch (*pch) + { + case L'"': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'"'; + break; + + case L'\\': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'\\'; + break; + + case L'/': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'/'; + break; + + case L'\b': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'b'; + break; + + case L'\f': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'f'; + break; + + case L'\n': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'n'; + break; + + case L'\r': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'r'; + break; + + case L'\t': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L't'; + break; + + default: + *pchTarget = *pch; + break; + } + + } + + *pchTarget = L'\"'; + ++pchTarget; + *pchTarget = L'\0'; + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define LocExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) +#define LocExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) +#define LocExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) +#define LocExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) +#define LocExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOCUTIL, e, x, s, __VA_ARGS__) +#define LocExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOCUTIL, g, x, s, __VA_ARGS__) + +// prototypes +static HRESULT ParseWxl( + __in IXMLDOMDocument* pixd, + __out WIX_LOCALIZATION** ppWixLoc + ); +static HRESULT ParseWxlStrings( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlControls( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlString( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlControl( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ); + +// from Winnls.h +#ifndef MUI_LANGUAGE_ID +#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention +#endif +#ifndef MUI_MERGE_USER_FALLBACK +#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages +#endif +#ifndef MUI_MERGE_SYSTEM_FALLBACK +#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages +#endif +typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( + __in DWORD dwFlags, + __out PULONG pulNumLanguages, + __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, + __inout PULONG pcchLanguagesBuffer +); + +extern "C" HRESULT DAPI LocProbeForFile( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczProbePath = NULL; + LANGID langid = 0; + LPWSTR sczLangIdFile = NULL; + LPWSTR sczLangsBuff = NULL; + GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = + reinterpret_cast( + GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); + + // If a language was specified, look for a loc file in that as a directory. + if (wzLanguage && *wzLanguage) + { + hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat base path to language."); + + hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat loc file name to probe path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + if (pvfnGetThreadPreferredUILanguages) + { + ULONG nLangs; + ULONG cchLangs = 0; + DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) + { + LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); + } + + hr = StrAlloc(&sczLangsBuff, cchLangs); + LocExitOnFailure(hr, "Failed to allocate buffer for languages"); + + nLangs = 0; + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) + { + LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); + } + + LPWSTR szLangs = sczLangsBuff; + for (ULONG i = 0; i < nLangs; ++i, szLangs += 5) + { + // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value. + hr = StrHexDecode(szLangs, reinterpret_cast(&langid), sizeof(langid)); + LocExitOnFailure(hr, "Failed to parse langId."); + + langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid)); + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user preferred langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user preferred langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + } + + langid = ::GetUserDefaultUILanguage(); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) + { + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + langid = ::GetSystemDefaultUILanguage(); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format system langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat system langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) + { + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + // Finally, look for the loc file in the base path. + hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat loc file name to base path."); + + if (!FileExistsEx(sczProbePath, NULL)) + { + hr = E_FILENOTFOUND; + } + +LExit: + if (SUCCEEDED(hr)) + { + hr = StrAllocString(psczPath, sczProbePath, 0); + } + + ReleaseStr(sczLangIdFile); + ReleaseStr(sczProbePath); + ReleaseStr(sczLangsBuff); + + return hr; +} + +extern "C" HRESULT DAPI LocLoadFromFile( + __in_z LPCWSTR wzWxlFile, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixd = NULL; + + hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd); + LocExitOnFailure(hr, "Failed to load WXL file as XML document."); + + hr = ParseWxl(pixd, ppWixLoc); + LocExitOnFailure(hr, "Failed to parse WXL."); + +LExit: + ReleaseObject(pixd); + + return hr; +} + +extern "C" HRESULT DAPI LocLoadFromResource( + __in HMODULE hModule, + __in_z LPCSTR szResource, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + LPVOID pvResource = NULL; + DWORD cbResource = 0; + LPWSTR sczXml = NULL; + IXMLDOMDocument* pixd = NULL; + + hr = ResReadData(hModule, szResource, &pvResource, &cbResource); + LocExitOnFailure(hr, "Failed to read theme from resource."); + + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); + LocExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + + hr = XmlLoadDocument(sczXml, &pixd); + LocExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = ParseWxl(pixd, ppWixLoc); + LocExitOnFailure(hr, "Failed to parse WXL."); + +LExit: + ReleaseObject(pixd); + ReleaseStr(sczXml); + + return hr; +} + +extern "C" void DAPI LocFree( + __in_opt WIX_LOCALIZATION* pWixLoc + ) +{ + if (pWixLoc) + { + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) + { + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); + } + + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) + { + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); + ReleaseStr(pWixLoc->rgLocControls[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocStrings); + ReleaseMem(pWixLoc->rgLocControls); + ReleaseMem(pWixLoc); + } +} + +extern "C" HRESULT DAPI LocLocalizeString( + __in const WIX_LOCALIZATION* pWixLoc, + __inout LPWSTR* ppsczInput + ) +{ + Assert(ppsczInput && pWixLoc); + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) + { + hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText); + LocExitOnFailure(hr, "Localizing string failed."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LocGetControl( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_CONTROL** ppLocControl + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + + for (DWORD i = 0; i < pWixLoc->cLocControls; ++i) + { + pLocControl = &pWixLoc->rgLocControls[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1)) + { + *ppLocControl = pLocControl; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LocGetString( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_STRING** ppLocString + ) +{ + HRESULT hr = E_NOTFOUND; + LOC_STRING* pLocString = NULL; + + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) + { + pLocString = pWixLoc->rgLocStrings + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1)) + { + *ppLocString = pLocString; + hr = S_OK; + break; + } + } + + return hr; +} + +extern "C" HRESULT DAPI LocAddString( + __in WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzLocString, + __in BOOL bOverridable + ) +{ + HRESULT hr = S_OK; + + ++pWixLoc->cLocStrings; + pWixLoc->rgLocStrings = static_cast(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); + LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings."); + + LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1); + + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId); + LocExitOnFailure(hr, "Failed to set localization string Id."); + + hr = StrAllocString(&pLocString->wzText, wzLocString, 0); + LocExitOnFailure(hr, "Failed to set localization string Text."); + + pLocString->bOverridable = bOverridable; + +LExit: + return hr; +} + +// helper functions + +static HRESULT ParseWxl( + __in IXMLDOMDocument* pixd, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMElement *pWxlElement = NULL; + WIX_LOCALIZATION* pWixLoc = NULL; + + pWixLoc = static_cast(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE)); + LocExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file."); + + // read the WixLocalization tag + hr = pixd->get_documentElement(&pWxlElement); + LocExitOnFailure(hr, "Failed to get localization element."); + + // get the Language attribute if present + pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET; + hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId); + if (S_FALSE == hr) + { + hr = S_OK; + } + LocExitOnFailure(hr, "Failed to get Language value."); + + // store the strings and controls in a node list + hr = ParseWxlStrings(pWxlElement, pWixLoc); + LocExitOnFailure(hr, "Parsing localization strings failed."); + + hr = ParseWxlControls(pWxlElement, pWixLoc); + LocExitOnFailure(hr, "Parsing localization controls failed."); + + *ppWixLoc = pWixLoc; + pWixLoc = NULL; + +LExit: + ReleaseObject(pWxlElement); + ReleaseMem(pWixLoc); + + return hr; +} + + +static HRESULT ParseWxlStrings( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + IXMLDOMNodeList* pixnl = NULL; + DWORD dwIdx = 0; + + hr = XmlSelectNodes(pElement, L"String", &pixnl); + LocExitOnLastError(hr, "Failed to get String child nodes of Wxl File."); + + hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocStrings)); + LocExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File."); + + if (0 < pWixLoc->cLocStrings) + { + pWixLoc->rgLocStrings = static_cast(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); + LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = ParseWxlString(pixn, dwIdx, pWixLoc); + LocExitOnFailure(hr, "Failed to parse localization string."); + + ++dwIdx; + ReleaseNullObject(pixn); + } + + hr = S_OK; + LocExitOnFailure(hr, "Failed to enumerate all localization strings."); + } + +LExit: + if (FAILED(hr) && pWixLoc->rgLocStrings) + { + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) + { + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocStrings); + } + + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + +static HRESULT ParseWxlControls( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + IXMLDOMNodeList* pixnl = NULL; + DWORD dwIdx = 0; + + hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl); + LocExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); + + hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocControls)); + LocExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); + + if (0 < pWixLoc->cLocControls) + { + pWixLoc->rgLocControls = static_cast(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE)); + LocExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = ParseWxlControl(pixn, dwIdx, pWixLoc); + LocExitOnFailure(hr, "Failed to parse localized control."); + + ++dwIdx; + ReleaseNullObject(pixn); + } + + hr = S_OK; + LocExitOnFailure(hr, "Failed to enumerate all localized controls."); + } + +LExit: + if (FAILED(hr) && pWixLoc->rgLocControls) + { + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) + { + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); + ReleaseStr(pWixLoc->rgLocControls[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocControls); + } + + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + +static HRESULT ParseWxlString( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_STRING* pLocString = NULL; + BSTR bstrText = NULL; + + pLocString = pWixLoc->rgLocStrings + dwIdx; + + // Id + hr = XmlGetAttribute(pixn, L"Id", &bstrText); + LocExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file."); + + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText); + LocExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file."); + + ReleaseNullBSTR(bstrText); + + // Overrideable + hr = XmlGetAttribute(pixn, L"Overridable", &bstrText); + LocExitOnFailure(hr, "Failed to get Xml attribute Overridable."); + + if (S_OK == hr) + { + pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1); + } + + ReleaseNullBSTR(bstrText); + + // Text + hr = XmlGetText(pixn, &bstrText); + LocExitOnFailure(hr, "Failed to get Xml text in Wxl file."); + + hr = StrAllocString(&pLocString->wzText, bstrText, 0); + LocExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file."); + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + +static HRESULT ParseWxlControl( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + BSTR bstrText = NULL; + + pLocControl = pWixLoc->rgLocControls + dwIdx; + + // Id + hr = XmlGetAttribute(pixn, L"Control", &bstrText); + LocExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file."); + + hr = StrAllocString(&pLocControl->wzControl, bstrText, 0); + LocExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file."); + + ReleaseNullBSTR(bstrText); + + // X + pLocControl->nX = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pLocControl->nX)); + LocExitOnFailure(hr, "Failed to get control X attribute."); + + // Y + pLocControl->nY = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pLocControl->nY)); + LocExitOnFailure(hr, "Failed to get control Y attribute."); + + // Width + pLocControl->nWidth = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pLocControl->nWidth)); + LocExitOnFailure(hr, "Failed to get control width attribute."); + + // Height + pLocControl->nHeight = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pLocControl->nHeight)); + LocExitOnFailure(hr, "Failed to get control height attribute."); + + // Text + hr = XmlGetText(pixn, &bstrText); + LocExitOnFailure(hr, "Failed to get control text in Wxl file."); + + hr = StrAllocString(&pLocControl->wzText, bstrText, 0); + LocExitOnFailure(hr, "Failed to duplicate control text in Wxl file."); + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define LoguExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) +#define LoguExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) +#define LoguExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) +#define LoguExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) +#define LoguExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOGUTIL, e, x, s, __VA_ARGS__) +#define LoguExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOGUTIL, g, x, s, __VA_ARGS__) + +// globals +static HMODULE LogUtil_hModule = NULL; +static BOOL LogUtil_fDisabled = FALSE; +static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE; +static LPWSTR LogUtil_sczLogPath = NULL; +static LPSTR LogUtil_sczPreInitBuffer = NULL; +static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD; +static CRITICAL_SECTION LogUtil_csLog = { }; +static BOOL LogUtil_fInitializedCriticalSection = FALSE; + +// Customization of certain parts of the string, within a line +static LPWSTR LogUtil_sczSpecialBeginLine = NULL; +static LPWSTR LogUtil_sczSpecialEndLine = NULL; +static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL; + +static LPCSTR LOGUTIL_UNKNOWN = "unknown"; +static LPCSTR LOGUTIL_WARNING = "warning"; +static LPCSTR LOGUTIL_STANDARD = "standard"; +static LPCSTR LOGUTIL_VERBOSE = "verbose"; +static LPCSTR LOGUTIL_DEBUG = "debug"; +static LPCSTR LOGUTIL_NONE = "none"; + +// prototypes +static HRESULT LogIdWork( + __in REPORT_LEVEL rl, + __in_opt HMODULE hModule, + __in DWORD dwLogId, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ); +static HRESULT LogStringWorkArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ); +static HRESULT LogStringWork( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_z LPCWSTR sczString, + __in BOOL fLOGUTIL_NEWLINE + ); + +// Hook to allow redirecting LogStringWorkRaw function calls +static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL; +static LPVOID s_vpvLogStringWorkRawContext = NULL; + + +/******************************************************************** + IsLogInitialized - Checks if log is currently initialized. +********************************************************************/ +extern "C" BOOL DAPI IsLogInitialized() +{ + return LogUtil_fInitializedCriticalSection; +} + +/******************************************************************** + IsLogOpen - Checks if log is currently initialized and open. +********************************************************************/ +extern "C" BOOL DAPI IsLogOpen() +{ + return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath); +} + + +/******************************************************************** + LogInitialize - initializes the logutil API + +********************************************************************/ +extern "C" void DAPI LogInitialize( + __in HMODULE hModule + ) +{ + AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called."); + + LogUtil_hModule = hModule; + LogUtil_fDisabled = FALSE; + + ::InitializeCriticalSection(&LogUtil_csLog); + LogUtil_fInitializedCriticalSection = TRUE; +} + + +/******************************************************************** + LogOpen - creates an application log file + + NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name +********************************************************************/ +extern "C" HRESULT DAPI LogOpen( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzLog, + __in_z_opt LPCWSTR wzPostfix, + __in_z_opt LPCWSTR wzExt, + __in BOOL fAppend, + __in BOOL fHeader, + __out_z_opt LPWSTR* psczLogPath + ) +{ + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + LPWSTR sczLogDirectory = NULL; + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + if (wzExt && *wzExt) + { + hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog); + LoguExitOnFailure(hr, "Failed to create log based on current system time."); + } + else + { + hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath); + LoguExitOnFailure(hr, "Failed to combine the log path."); + + hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory); + LoguExitOnFailure(hr, "Failed to get log directory."); + + hr = DirEnsureExists(sczLogDirectory, NULL); + LoguExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory); + + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + } + + if (fAppend) + { + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); + } + } + + LogUtil_fDisabled = FALSE; + + if (fHeader) + { + LogHeader(); + } + + if (NULL != LogUtil_sczPreInitBuffer) + { + // Log anything that was logged before LogOpen() was called. + LogStringWorkRaw(LogUtil_sczPreInitBuffer); + ReleaseNullStr(LogUtil_sczPreInitBuffer); + } + + if (psczLogPath) + { + hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0); + LoguExitOnFailure(hr, "Failed to copy log path."); + } + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + ReleaseStr(sczLogDirectory); + + return hr; +} + + +/******************************************************************** + LogDisable - closes any open files and disables in memory logging. + +********************************************************************/ +void DAPI LogDisable() +{ + ::EnterCriticalSection(&LogUtil_csLog); + + LogUtil_fDisabled = TRUE; + + ReleaseFileHandle(LogUtil_hLog); + ReleaseNullStr(LogUtil_sczLogPath); + ReleaseNullStr(LogUtil_sczPreInitBuffer); + + ::LeaveCriticalSection(&LogUtil_csLog); +} + + +/******************************************************************** + LogRedirect - Redirects all logging strings to the specified + function - or set NULL to disable the hook +********************************************************************/ +void DAPI LogRedirect( + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, + __in_opt LPVOID pvContext + ) +{ + s_vpfLogStringWorkRaw = vpfLogStringWorkRaw; + s_vpvLogStringWorkRawContext = pvContext; +} + + +/******************************************************************** + LogRename - Renames a logfile, moving its contents to a new path, + and re-opening the file for appending at the new + location +********************************************************************/ +HRESULT DAPI LogRename( + __in_z LPCWSTR wzNewPath + ) +{ + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + ReleaseFileHandle(LogUtil_hLog); + + hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE); + LoguExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath); + + hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0); + LoguExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath); + + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + } + + // Enable "append" mode by moving file pointer to the end + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + return hr; +} + + +extern "C" void DAPI LogClose( + __in BOOL fFooter + ) +{ + if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter) + { + LogFooter(); + } + + ReleaseFileHandle(LogUtil_hLog); + ReleaseNullStr(LogUtil_sczLogPath); + ReleaseNullStr(LogUtil_sczPreInitBuffer); +} + + +extern "C" void DAPI LogUninitialize( + __in BOOL fFooter + ) +{ + LogClose(fFooter); + + if (LogUtil_fInitializedCriticalSection) + { + ::DeleteCriticalSection(&LogUtil_csLog); + LogUtil_fInitializedCriticalSection = FALSE; + } + + LogUtil_hModule = NULL; + LogUtil_fDisabled = FALSE; + + ReleaseNullStr(LogUtil_sczSpecialBeginLine); + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); + ReleaseNullStr(LogUtil_sczSpecialEndLine); +} + + +/******************************************************************** + LogIsOpen - returns whether log file is open or note + +********************************************************************/ +extern "C" BOOL DAPI LogIsOpen() +{ + return INVALID_HANDLE_VALUE != LogUtil_hLog; +} + + +/******************************************************************** + LogSetSpecialParams - sets a special beginline string, endline + string, post-timestamp string, etc. +********************************************************************/ +HRESULT DAPI LogSetSpecialParams( + __in_z_opt LPCWSTR wzSpecialBeginLine, + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, + __in_z_opt LPCWSTR wzSpecialEndLine + ) +{ + HRESULT hr = S_OK; + + // Handle special string to be prepended before every full line + if (NULL == wzSpecialBeginLine) + { + ReleaseNullStr(LogUtil_sczSpecialBeginLine); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0); + LoguExitOnFailure(hr, "Failed to allocate copy of special beginline string"); + } + + // Handle special string to be appended to every time stamp + if (NULL == wzSpecialAfterTimeStamp) + { + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0); + LoguExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string"); + } + + // Handle special string to be appended before every full line + if (NULL == wzSpecialEndLine) + { + ReleaseNullStr(LogUtil_sczSpecialEndLine); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0); + LoguExitOnFailure(hr, "Failed to allocate copy of special endline string"); + } + +LExit: + return hr; +} + +/******************************************************************** + LogSetLevel - sets the logging level + + NOTE: returns previous logging level +********************************************************************/ +extern "C" REPORT_LEVEL DAPI LogSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fLogChange + ) +{ + AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set"); + + REPORT_LEVEL rlPrev = LogUtil_rlCurrent; + + if (LogUtil_rlCurrent != rl) + { + LogUtil_rlCurrent = rl; + + if (fLogChange) + { + LPCSTR szLevel = LOGUTIL_UNKNOWN; + switch (LogUtil_rlCurrent) + { + case REPORT_WARNING: + szLevel = LOGUTIL_WARNING; + break; + case REPORT_STANDARD: + szLevel = LOGUTIL_STANDARD; + break; + case REPORT_VERBOSE: + szLevel = LOGUTIL_VERBOSE; + break; + case REPORT_DEBUG: + szLevel = LOGUTIL_DEBUG; + break; + case REPORT_NONE: + szLevel = LOGUTIL_NONE; + break; + } + + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); + } + } + + return rlPrev; +} + + +/******************************************************************** + LogGetLevel - gets the current logging level + +********************************************************************/ +extern "C" REPORT_LEVEL DAPI LogGetLevel() +{ + return LogUtil_rlCurrent; +} + + +/******************************************************************** + LogGetPath - gets the current log path + +********************************************************************/ +extern "C" HRESULT DAPI LogGetPath( + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, + __in DWORD cchLogPath + ) +{ + Assert(pwzLogPath); + + HRESULT hr = S_OK; + + if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one! + { + ExitFunction1(hr = E_UNEXPECTED); + } + + hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath); + +LExit: + return hr; +} + + +/******************************************************************** + LogGetHandle - gets the current log file handle + +********************************************************************/ +extern "C" HANDLE DAPI LogGetHandle() +{ + return LogUtil_hLog; +} + + +/******************************************************************** + LogString - write a string to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogString( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = LogStringArgs(rl, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogStringArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogStringWorkArgs(rl, szFormat, args, FALSE); + +LExit: + return hr; +} + +/******************************************************************** + LogStringLine - write a string plus LOGUTIL_NEWLINE to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogStringLine( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = LogStringLineArgs(rl, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogStringLineArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogStringWorkArgs(rl, szFormat, args, TRUE); + +LExit: + return hr; +} + +/******************************************************************** + LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log + + NOTE: uses format string from MESSAGETABLE resource +********************************************************************/ + +extern "C" HRESULT DAPI LogIdModuleArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LogIdModule( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + ... + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + va_list args; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + va_start(args, hModule); + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); + va_end(args); + +LExit: + return hr; +} + + + + +/******************************************************************** + LogError - write an error to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogErrorString( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + + va_list args; + va_start(args, szFormat); + hr = LogErrorStringArgs(hrError, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogErrorStringArgs( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFormat = NULL; + LPWSTR sczMessage = NULL; + + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); + LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); + + // format the string as a unicode string - this is necessary to be able to include + // international characters in our output string. This does have the counterintuitive effect + // that the caller's "%s" is interpreted differently + // (so callers should use %hs for LPSTR and %ls for LPWSTR) + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); + LoguExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat); + + hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage); + +LExit: + ReleaseStr(sczFormat); + ReleaseStr(sczMessage); + + return hr; +} + + +/******************************************************************** + LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log + + NOTE: uses format string from MESSAGETABLE resource + can log no more than three strings in the error message +********************************************************************/ +extern "C" HRESULT DAPI LogErrorIdModule( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzString1 = NULL, + __in_z_opt LPCWSTR wzString2 = NULL, + __in_z_opt LPCWSTR wzString3 = NULL + ) +{ + HRESULT hr = S_OK; + WCHAR wzError[11]; + WORD cStrings = 1; // guaranteed wzError is in the list + + hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError); + LoguExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError); + + cStrings += wzString1 ? 1 : 0; + cStrings += wzString2 ? 1 : 0; + cStrings += wzString3 ? 1 : 0; + + hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3); + LoguExitOnFailure(hr, "Failed to log id module."); + +LExit: + return hr; +} + +/******************************************************************** + LogHeader - write a standard header to the log + +********************************************************************/ +extern "C" HRESULT DAPI LogHeader() +{ + HRESULT hr = S_OK; + WCHAR wzComputerName[MAX_PATH]; + DWORD cchComputerName = countof(wzComputerName); + WCHAR wzPath[MAX_PATH]; + DWORD dwMajorVersion = 0; + DWORD dwMinorVersion = 0; + LPCSTR szLevel = LOGUTIL_UNKNOWN; + LPWSTR sczCurrentDateTime = NULL; + + // + // get the interesting data + // + if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath))) + { + memset(wzPath, 0, sizeof(wzPath)); + } + + hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion); + if (FAILED(hr)) + { + dwMajorVersion = 0; + dwMinorVersion = 0; + } + + if (!::GetComputerNameW(wzComputerName, &cchComputerName)) + { + ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName)); + } + + TimeCurrentDateTime(&sczCurrentDateTime, FALSE); + + // + // write data to the log + // + LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime); + LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF); + LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName); + switch (LogUtil_rlCurrent) + { + case REPORT_WARNING: + szLevel = LOGUTIL_WARNING; + break; + case REPORT_STANDARD: + szLevel = LOGUTIL_STANDARD; + break; + case REPORT_VERBOSE: + szLevel = LOGUTIL_VERBOSE; + break; + case REPORT_DEBUG: + szLevel = LOGUTIL_DEBUG; + break; + case REPORT_NONE: + szLevel = LOGUTIL_NONE; + break; + } + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); + + hr = S_OK; + + ReleaseStr(sczCurrentDateTime); + + return hr; +} + + +/******************************************************************** + LogFooterWork - write a standard footer to the log + +********************************************************************/ + +static HRESULT LogFooterWork( + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + + va_list args; + va_start(args, szFormat); + hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogFooter() +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentDateTime = NULL; + TimeCurrentDateTime(&sczCurrentDateTime, FALSE); + hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime); + ReleaseStr(sczCurrentDateTime); + return hr; +} + +/******************************************************************** + LogStringWorkRaw - Write a raw, unformatted string to the log + +********************************************************************/ +extern "C" HRESULT LogStringWorkRaw( + __in_z LPCSTR szLogData + ) +{ + Assert(szLogData && *szLogData); + + HRESULT hr = S_OK; + size_t cchLogData = 0; + DWORD cbLogData = 0; + DWORD cbTotal = 0; + DWORD cbWrote = 0; + + hr = ::StringCchLengthA(szLogData, STRSAFE_MAX_CCH, &cchLogData); + LoguExitOnRootFailure(hr, "Failed to get length of raw string"); + + cbLogData = (DWORD)cchLogData; + + // If the log hasn't been initialized yet, store it in a buffer + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0); + LoguExitOnFailure(hr, "Failed to concatenate string to pre-init buffer"); + + ExitFunction1(hr = S_OK); + } + + // write the string + while (cbTotal < cbLogData) + { + if (!::WriteFile(LogUtil_hLog, reinterpret_cast(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL)) + { + LoguExitOnLastError(hr, "Failed to write output to log: %ls - %hs", LogUtil_sczLogPath, szLogData); + } + + cbTotal += cbWrote; + } + +LExit: + return hr; +} + +// +// private worker functions +// +static HRESULT LogIdWork( + __in REPORT_LEVEL rl, + __in_opt HMODULE hModule, + __in DWORD dwLogId, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + DWORD cch = 0; + + // get the string for the id +#pragma prefast(push) +#pragma prefast(disable:25028) +#pragma prefast(disable:25068) + cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, + static_cast(hModule), dwLogId, 0, reinterpret_cast(&pwz), 0, &args); +#pragma prefast(pop) + + if (0 == cch) + { + LoguExitOnLastError(hr, "failed to log id: %d", dwLogId); + } + + if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1]) + { + pwz[cch-2] = L'\0'; // remove newline from message table + } + + LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE); + +LExit: + if (pwz) + { + ::LocalFree(pwz); + } + + return hr; +} + + +static HRESULT LogStringWorkArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + Assert(szFormat && *szFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormat = NULL; + LPWSTR sczMessage = NULL; + + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); + LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); + + // format the string as a unicode string + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); + LoguExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat); + + hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE); + LoguExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage); + +LExit: + ReleaseStr(sczFormat); + ReleaseStr(sczMessage); + + return hr; +} + + +static HRESULT LogStringWork( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_z LPCWSTR sczString, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + Assert(sczString && *sczString); + + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + LPWSTR scz = NULL; + LPCWSTR wzLogData = NULL; + LPSTR sczMultiByte = NULL; + + // If logging is disabled, just bail. + if (LogUtil_fDisabled) + { + ExitFunction(); + } + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + if (fLOGUTIL_NEWLINE) + { + // get the process and thread id. + DWORD dwProcessId = ::GetCurrentProcessId(); + DWORD dwThreadId = ::GetCurrentThreadId(); + + // get the time relative to GMT. + SYSTEMTIME st = { }; + ::GetLocalTime(&st); + + DWORD dwId = dwLogId & 0xFFFFFFF; + DWORD dwType = dwLogId & 0xF0000000; + LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i"; + + // add line prefix and trailing newline + hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"", + dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId, + LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n"); + LoguExitOnFailure(hr, "Failed to format line prefix."); + } + + wzLogData = scz ? scz : sczString; + + // Convert to UTF-8 before writing out to the log file + hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8); + LoguExitOnFailure(hr, "Failed to convert log string to UTF-8"); + + if (s_vpfLogStringWorkRaw) + { + hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext); + LoguExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString); + } + else + { + hr = LogStringWorkRaw(sczMultiByte); + LoguExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString); + } + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + ReleaseStr(scz); + ReleaseStr(sczMultiByte); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define MemExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) +#define MemExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) +#define MemExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) +#define MemExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) +#define MemExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MEMUTIL, e, x, s, __VA_ARGS__) +#define MemExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MEMUTIL, g, x, s, __VA_ARGS__) + + +#if DEBUG +static BOOL vfMemInitialized = FALSE; +#endif + +extern "C" HRESULT DAPI MemInitialize() +{ +#if DEBUG + vfMemInitialized = TRUE; +#endif + return S_OK; +} + +extern "C" void DAPI MemUninitialize() +{ +#if DEBUG + vfMemInitialized = FALSE; +#endif +} + +extern "C" LPVOID DAPI MemAlloc( + __in SIZE_T cbSize, + __in BOOL fZero + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(0 < cbSize, "MemAlloc() called with invalid size"); + return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize); +} + + +extern "C" LPVOID DAPI MemReAlloc( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(0 < cbSize, "MemReAlloc() called with invalid size"); + return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize); +} + + +extern "C" HRESULT DAPI MemReAllocSecure( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero, + __deref_out LPVOID* ppvNew + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer"); + AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size"); + + HRESULT hr = S_OK; + DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY; + LPVOID pvNew = NULL; + + dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0; + pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize); + if (!pvNew) + { + pvNew = MemAlloc(cbSize, fZero); + if (pvNew) + { + const SIZE_T cbCurrent = MemSize(pv); + if (-1 == cbCurrent) + { + MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); + } + + // HeapReAlloc may allocate more memory than requested. + const SIZE_T cbNew = MemSize(pvNew); + if (-1 == cbNew) + { + MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); + } + + cbSize = cbNew; + if (cbSize > cbCurrent) + { + cbSize = cbCurrent; + } + + memcpy_s(pvNew, cbNew, pv, cbSize); + + SecureZeroMemory(pv, cbCurrent); + MemFree(pv); + } + } + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory"); + + *ppvNew = pvNew; + pvNew = NULL; + +LExit: + ReleaseMem(pvNew); + + return hr; +} + + +extern "C" HRESULT DAPI MemAllocArray( + __inout LPVOID* ppvArray, + __in SIZE_T cbArrayType, + __in DWORD dwItemCount + ) +{ + return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount); +} + + +extern "C" HRESULT DAPI MemReAllocArray( + __inout LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwNewItemCount + ) +{ + HRESULT hr = S_OK; + DWORD cNew = 0; + LPVOID pvNew = NULL; + SIZE_T cbNew = 0; + + hr = ::DWordAdd(cArray, dwNewItemCount, &cNew); + MemExitOnFailure(hr, "Integer overflow when calculating new element count."); + + hr = ::SIZETMult(cNew, cbArrayType, &cbNew); + MemExitOnFailure(hr, "Integer overflow when calculating new block size."); + + if (*ppvArray) + { + SIZE_T cbCurrent = MemSize(*ppvArray); + if (cbCurrent < cbNew) + { + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array."); + + *ppvArray = pvNew; + } + } + else + { + pvNew = MemAlloc(cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + + *ppvArray = pvNew; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI MemEnsureArraySize( + __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ) +{ + HRESULT hr = S_OK; + DWORD cNew = 0; + LPVOID pvNew = NULL; + SIZE_T cbNew = 0; + + hr = ::DWordAdd(cArray, dwGrowthCount, &cNew); + MemExitOnFailure(hr, "Integer overflow when calculating new element count."); + + hr = ::SIZETMult(cNew, cbArrayType, &cbNew); + MemExitOnFailure(hr, "Integer overflow when calculating new block size."); + + if (*ppvArray) + { + SIZE_T cbUsed = cArray * cbArrayType; + SIZE_T cbCurrent = MemSize(*ppvArray); + if (cbCurrent < cbUsed) + { + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger."); + + *ppvArray = pvNew; + } + } + else + { + pvNew = MemAlloc(cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + + *ppvArray = pvNew; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI MemInsertIntoArray( + __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __in DWORD dwInsertIndex, + __in DWORD cInsertItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ) +{ + HRESULT hr = S_OK; + DWORD i; + BYTE *pbArray = NULL; + + if (0 == cInsertItems) + { + ExitFunction1(hr = S_OK); + } + + hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount); + MemExitOnFailure(hr, "Failed to resize array while inserting items"); + + pbArray = reinterpret_cast(*ppvArray); + for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i) + { + memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType); + } + + // Zero out the newly-inserted items + memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType); + +LExit: + return hr; +} + +extern "C" void DAPI MemRemoveFromArray( + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __in DWORD dwRemoveIndex, + __in DWORD cRemoveItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in BOOL fPreserveOrder + ) +{ + BYTE *pbArray = static_cast(pvArray); + DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex); + + if (fPreserveOrder) + { + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType); + } + else + { + DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems); + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType); + } + + ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType); +} + +extern "C" void DAPI MemArraySwapItems( + __inout_bcount(cbArrayType) LPVOID pvArray, + __in DWORD dwIndex1, + __in DWORD dwIndex2, + __in SIZE_T cbArrayType + ) +{ + BYTE *pbArrayItem1 = static_cast(pvArray) + dwIndex1 * cbArrayType; + BYTE *pbArrayItem2 = static_cast(pvArray) + dwIndex2 * cbArrayType; + DWORD dwByteIndex = 0; + + if (dwIndex1 == dwIndex2) + { + return; + } + + // Use XOR swapping to avoid the need for a temporary item + while (dwByteIndex < cbArrayType) + { + // Try to do many bytes at a time in most cases + if (cbArrayType - dwByteIndex > sizeof(DWORD64)) + { + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // y: X xor Y + *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + + dwByteIndex += sizeof(DWORD64); + } + else + { + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // y: X xor Y + *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + + dwByteIndex += sizeof(unsigned char); + } + } +} + +extern "C" HRESULT DAPI MemFree( + __in LPVOID pv + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()); +} + + +extern "C" SIZE_T DAPI MemSize( + __in LPCVOID pv + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + return ::HeapSize(::GetProcessHeap(), 0, pv); +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// okay, this may look a little weird, but metautil.h cannot be in the +// pre-compiled header because we need to #define these things so the +// correct GUID's get pulled into this object file +#include +#include "metautil.h" + + +// Exit macros +#define MetaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) +#define MetaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) +#define MetaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) +#define MetaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) +#define MetaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_METAUTIL, e, x, s, __VA_ARGS__) +#define MetaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_METAUTIL, g, x, s, __VA_ARGS__) + + +// prototypes +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ); + + +/******************************************************************** + MetaFindWebBase - finds a metabase base string that matches IP, Port and Header + +********************************************************************/ +extern "C" HRESULT DAPI MetaFindWebBase( + __in IMSAdminBaseW* piMetabase, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase && cchWebBase); + + HRESULT hr = S_OK; + + BOOL fFound = FALSE; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + DWORD dwIndex = 0; + + METADATA_RECORD mr; + METADATA_RECORD mrAddress; + + LPWSTR pwzExists = NULL; + LPWSTR pwzIPExists = NULL; + LPWSTR pwzPortExists = NULL; + int iPortExists = 0; + LPCWSTR pwzHeaderExists = NULL; + + memset(&mr, 0, sizeof(mr)); + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = METADATA_INHERIT; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + + memset(&mrAddress, 0, sizeof(mrAddress)); + mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS; + mrAddress.dwMDAttributes = METADATA_INHERIT; + mrAddress.dwMDUserType = IIS_MD_UT_SERVER; + mrAddress.dwMDDataType = ALL_METADATA; + + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (FAILED(hr)) + break; + + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + MetaExitOnFailure(hr, "failed to get key from metabase while searching for web servers"); + + // if we have an IIsWebServer store the key + if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) + { + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress); + if (MD_ERROR_DATA_NOT_FOUND == hr) + hr = S_FALSE; + MetaExitOnFailure(hr, "failed to get address from metabase while searching for web servers"); + + // break down the first address into parts + pwzIPExists = reinterpret_cast(mrAddress.pbMDData); + pwzExists = wcsstr(pwzIPExists, L":"); + if (NULL == pwzExists) + continue; + + *pwzExists = L'\0'; + + pwzPortExists = pwzExists + 1; + pwzExists = wcsstr(pwzPortExists, L":"); + if (NULL == pwzExists) + continue; + + *pwzExists = L'\0'; + iPortExists = wcstol(pwzPortExists, NULL, 10); + + pwzHeaderExists = pwzExists + 1; + + // compare the passed in address with the address listed for this web + if (S_OK == hr && + (0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) && + iPort == iPortExists && + 0 == lstrcmpW(wzHeader, pwzHeaderExists)) + { + // if the passed in buffer wasn't big enough + hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey); + MetaExitOnFailure(hr, "failed to copy in web base: %ls", wzKey); + + fFound = TRUE; + break; + } + } + } + + if (E_NOMOREITEMS == hr) + { + Assert(!fFound); + hr = S_FALSE; + } + +LExit: + MetaFreeValue(&mrAddress); + MetaFreeValue(&mr); + + if (!fFound && SUCCEEDED(hr)) + hr = S_FALSE; + + return hr; +} + + +/******************************************************************** + MetaFindFreeWebBase - finds the next metabase base string + +********************************************************************/ +extern "C" HRESULT DAPI MetaFindFreeWebBase( + __in IMSAdminBaseW* piMetabase, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + DWORD dwSubKeys[100]; + int cSubKeys = 0; + DWORD dwIndex = 0; + + int i; + DWORD dwKey; + + METADATA_RECORD mr; + + memset(&mr, 0, sizeof(mr)); + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = 0; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = STRING_METADATA; + + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (FAILED(hr)) + break; + + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + MetaExitOnFailure(hr, "failed to get key from metabase while searching for free web root"); + + // if we have a IIsWebServer get the address information + if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast(mr.pbMDData))) + { + if (cSubKeys >= countof(dwSubKeys)) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + MetaExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites"); + } + + dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10); + ++cSubKeys; + Sort(dwSubKeys, cSubKeys); + } + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + MetaExitOnFailure(hr, "failed to find free web root"); + + // find the lowest free web root + dwKey = 1; + for (i = 0; i < cSubKeys; ++i) + { + if (dwKey < dwSubKeys[i]) + break; + + dwKey = dwSubKeys[i] + 1; + } + + hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey); +LExit: + MetaFreeValue(&mr); + return hr; +} + + +/******************************************************************** + MetaOpenKey - open key + +********************************************************************/ +extern "C" HRESULT DAPI MetaOpenKey( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __in DWORD dwAccess, + __in DWORD cRetries, + __out METADATA_HANDLE* pmh + ) +{ + Assert(piMetabase && pmh); + + HRESULT hr = S_OK; + + // loop while the key is busy + do + { + hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh); + if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr) + ::SleepEx(1000, TRUE); + } while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--); + + return hr; +} + + +/******************************************************************** + MetaGetValue - finds the next metabase base string + + NOTE: piMetabase is optional +********************************************************************/ +extern "C" HRESULT DAPI MetaGetValue( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __inout METADATA_RECORD* pmr + ) +{ + Assert(pmr); + + HRESULT hr = S_OK; + BOOL fInitialized = FALSE; + DWORD cbRequired = 0; + + if (!piMetabase) + { + hr = ::CoInitialize(NULL); + MetaExitOnFailure(hr, "failed to initialize COM"); + fInitialized = TRUE; + + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + MetaExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object"); + } + + if (!pmr->pbMDData) + { + pmr->dwMDDataLen = 256; + pmr->pbMDData = static_cast(MemAlloc(pmr->dwMDDataLen, TRUE)); + MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); + } + else // set the size of the data to the actual size of the memory + { + SIZE_T cb = MemSize(pmr->pbMDData); + if (cb > DWORD_MAX) + { + MetaExitOnRootFailure(hr = E_INVALIDSTATE, "metabase data is too large: %Iu", cb); + } + pmr->dwMDDataLen = (DWORD)cb; + } + + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); + if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) + { + pmr->dwMDDataLen = cbRequired; + BYTE* pb = static_cast(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE)); + MetaExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value"); + + pmr->pbMDData = pb; + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); + } + MetaExitOnFailure(hr, "failed to get metabase data"); + +LExit: + if (fInitialized) + { + ReleaseObject(piMetabase); + ::CoUninitialize(); + } + + return hr; +} + + +/******************************************************************** + MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue() + + NOTE: METADATA_RECORD must have been returned from MetaGetValue() above +********************************************************************/ +extern "C" void DAPI MetaFreeValue( + __in METADATA_RECORD* pmr + ) +{ + Assert(pmr); + + ReleaseNullMem(pmr->pbMDData); +} + + +// +// private +// + +/******************************************************************** + Sort - quick and dirty insertion sort + +********************************************************************/ +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ) +{ + int i, j; + DWORD dwData; + + for (i = 1; i < cArray; ++i) + { + dwData = dwArray[i]; + + j = i - 1; + while (0 <= j && dwArray[j] > dwData) + { + dwArray[j + 1] = dwArray[j]; + j--; + } + + dwArray[j + 1] = dwData; + } +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define MonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) +#define MonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) +#define MonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) +#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) +#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__) +#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__) + +const int MON_THREAD_GROWTH = 5; +const int MON_ARRAY_GROWTH = 40; +const int MON_MAX_MONITORS_PER_THREAD = 63; +const int MON_THREAD_INIT_RETRIES = 1000; +const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10; +const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute +const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently +const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000; +const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass"; + +enum MON_MESSAGE +{ + MON_MESSAGE_ADD = WM_APP + 1, + MON_MESSAGE_REMOVE, + MON_MESSAGE_REMOVED, // Sent by waiter thread back to coordinator thread to indicate a remove occurred + 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). + 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. + MON_MESSAGE_NETWORK_STATUS_UPDATE, // Some change to network connectivity occurred (a network connection was connected or disconnected for example) + MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any successful network waits. + // Annoyingly, this is necessary to catch the rare case that the remote server goes offline unexpectedly, such as by + // network cable unplugged or power loss - in this case there is no local network status change, and the wait will just never fire. + // So we very occasionally retry all successful network waits. When this occurs, we notify for changes, even though there may not have been any. + // 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. + // 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). + MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any failed network waits + MON_MESSAGE_DRIVE_STATUS_UPDATE, // Some change to local drive has occurred (new drive created or plugged in, or removed) + MON_MESSAGE_DRIVE_QUERY_REMOVE, // User wants to unplug a drive, which MonUtil will always allow + MON_MESSAGE_STOP +}; + +enum MON_TYPE +{ + MON_NONE = 0, + MON_DIRECTORY = 1, + MON_REGKEY = 2 +}; + +struct MON_REQUEST +{ + MON_TYPE type; + DWORD dwMaxSilencePeriodInMs; + + // Handle to the main window for RegisterDeviceNotification() (same handle as owned by coordinator thread) + HWND hwnd; + // and handle to the notification (specific to this request) + HDEVNOTIFY hNotify; + + BOOL fRecursive; + void *pvContext; + + HRESULT hrStatus; + + LPWSTR sczOriginalPathRequest; + BOOL fNetwork; // This reflects either a UNC or mounted drive original request + DWORD dwPathHierarchyIndex; + LPWSTR *rgsczPathHierarchy; + DWORD cPathHierarchy; + + // 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 + // after notification, we set fPendingFire back to FALSE + BOOL fPendingFire; + BOOL fSkipDeltaAdd; + DWORD dwSilencePeriodInMs; + + union + { + struct + { + } directory; + struct + { + HKEY hkRoot; + HKEY hkSubKey; + REG_KEY_BITNESS kbKeyBitness; // Only used to pass on 32-bit, 64-bit, or default parameter + } regkey; + }; +}; + +struct MON_ADD_MESSAGE +{ + MON_REQUEST request; + HANDLE handle; +}; + +struct MON_REMOVE_MESSAGE +{ + MON_TYPE type; + BOOL fRecursive; + + union + { + struct + { + LPWSTR sczDirectory; + } directory; + struct + { + HKEY hkRoot; + LPWSTR sczSubKey; + REG_KEY_BITNESS kbKeyBitness; + } regkey; + }; +}; + +struct MON_WAITER_CONTEXT +{ + DWORD dwCoordinatorThreadId; + + HANDLE hWaiterThread; + DWORD dwWaiterThreadId; + BOOL fWaiterThreadMessageQueueInitialized; + + // Callbacks + PFN_MONGENERAL vpfMonGeneral; + PFN_MONDIRECTORY vpfMonDirectory; + PFN_MONREGKEY vpfMonRegKey; + + // Context for callbacks + LPVOID pvContext; + + // HANDLEs are in their own array for easy use with WaitForMultipleObjects() + // After initialization, the very first handle is just to wake the listener thread to have it re-wait on a new list + // 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 + HANDLE *rgHandles; + DWORD cHandles; + + // Requested things to monitor + MON_REQUEST *rgRequests; + DWORD cRequests; + + // Number of pending notifications + DWORD cRequestsPending; + + // Number of requests in a failed state (couldn't initiate wait) + DWORD cRequestsFailing; +}; + +// Info stored about each waiter by the coordinator +struct MON_WAITER_INFO +{ + DWORD cMonitorCount; + + MON_WAITER_CONTEXT *pWaiterContext; +}; + +// This struct is used when Thread A wants to send a task to another thread B (and get notified when the task finishes) +// 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 +// (even long after sender is no longer waiting, in case thread has huge message queue) +// and you must send 2 parameters in the message: +// 1) a pointer to this struct (which is always valid) +// 2) the original value of dwIteration +// The receiver of the message can compare the current value of dwSendIteration in the struct with what was sent in the message +// If values are different, we're too late and thread A is no longer waiting on this response +// otherwise, set dwResponseIteration to the same value, and call ::SetEvent() on hWait +// Thread A will then wakeup, and must verify that dwResponseIteration == dwSendIteration to ensure it isn't an earlier out-of-date reply +// replying to a newer wait +// pvContext is used to send a misc parameter related to processing data +struct MON_INTERNAL_TEMPORARY_WAIT +{ + // Should be incremented each time sender sends a pointer to this struct, so each request has a different iteration + DWORD dwSendIteration; + DWORD dwReceiveIteration; + HANDLE hWait; + void *pvContext; +}; + +struct MON_STRUCT +{ + HANDLE hCoordinatorThread; + DWORD dwCoordinatorThreadId; + BOOL fCoordinatorThreadMessageQueueInitialized; + + // Invisible window for receiving network status & drive added/removal messages + HWND hwnd; + // Used by window procedure for sending request and waiting for response from waiter threads + // such as in event of a request to remove a device + MON_INTERNAL_TEMPORARY_WAIT internalWait; + + // Callbacks + PFN_MONGENERAL vpfMonGeneral; + PFN_MONDRIVESTATUS vpfMonDriveStatus; + PFN_MONDIRECTORY vpfMonDirectory; + PFN_MONREGKEY vpfMonRegKey; + + // Context for callbacks + LPVOID pvContext; + + // Waiter thread array + MON_WAITER_INFO *rgWaiterThreads; + DWORD cWaiterThreads; +}; + +const int MON_HANDLE_BYTES = sizeof(MON_STRUCT); + +static DWORD WINAPI CoordinatorThread( + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext + ); +// Initiates (or if *pHandle is non-null, continues) wait on the directory or subkey +// if the directory or subkey doesn't exist, instead calls it on the first existing parent directory or subkey +// writes to pRequest->dwPathHierarchyIndex with the array index that was waited on +static HRESULT InitiateWait( + __inout MON_REQUEST *pRequest, + __inout HANDLE *pHandle + ); +static DWORD WINAPI WaiterThread( + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext + ); +static void Notify( + __in HRESULT hr, + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REQUEST *pRequest + ); +static void MonRequestDestroy( + __in MON_REQUEST *pRequest + ); +static void MonAddMessageDestroy( + __in_opt MON_ADD_MESSAGE *pMessage + ); +static void MonRemoveMessageDestroy( + __in_opt MON_REMOVE_MESSAGE *pMessage + ); +static BOOL GetRecursiveFlag( + __in MON_REQUEST *pRequest, + __in DWORD dwIndex + ); +static HRESULT FindRequestIndex( + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REMOVE_MESSAGE *pMessage, + __out DWORD *pdwIndex + ); +static HRESULT RemoveRequest( + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex + ); +static REGSAM GetRegKeyBitness( + __in MON_REQUEST *pRequest + ); +static HRESULT DuplicateRemoveMessage( + __in MON_REMOVE_MESSAGE *pMessage, + __out MON_REMOVE_MESSAGE **ppMessage + ); +static LRESULT CALLBACK MonWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static HRESULT CreateMonWindow( + __in MON_STRUCT *pm, + __out HWND *pHwnd + ); +// if *phMonitor is non-NULL, closes the old wait before re-starting the new wait +static HRESULT WaitForNetworkChanges( + __inout HANDLE *phMonitor, + __in MON_STRUCT *pm + ); +static HRESULT UpdateWaitStatus( + __in HRESULT hrNewStatus, + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex, + __out_opt DWORD *pdwNewRequestIndex + ); + +extern "C" HRESULT DAPI MonCreate( + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, + __in PFN_MONGENERAL vpfMonGeneral, + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, + __in_opt PFN_MONDIRECTORY vpfMonDirectory, + __in_opt PFN_MONREGKEY vpfMonRegKey, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD dwRetries = MON_THREAD_INIT_RETRIES; + + MonExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor"); + + // Allocate the struct + *pHandle = static_cast(MemAlloc(sizeof(MON_STRUCT), TRUE)); + MonExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object"); + + MON_STRUCT *pm = static_cast(*pHandle); + + pm->vpfMonGeneral = vpfMonGeneral; + pm->vpfMonDriveStatus = vpfMonDriveStatus; + pm->vpfMonDirectory = vpfMonDirectory; + pm->vpfMonRegKey = vpfMonRegKey; + pm->pvContext = pvContext; + + pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId); + if (!pm->hCoordinatorThread) + { + MonExitWithLastError(hr, "Failed to create waiter thread."); + } + + // 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. + while (!pm->fCoordinatorThreadMessageQueueInitialized && 0 < dwRetries) + { + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); + --dwRetries; + } + + if (0 == dwRetries) + { + hr = E_UNEXPECTED; + MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI MonAddDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzDirectory, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvDirectoryContext + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczDirectory = NULL; + LPWSTR sczOriginalPathRequest = NULL; + MON_ADD_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0); + MonExitOnFailure(hr, "Failed to convert directory string to UNC path"); + + hr = PathBackslashTerminate(&sczOriginalPathRequest); + MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\') + { + pMessage->request.fNetwork = TRUE; + } + else + { + hr = UncConvertFromMountedDrive(&sczDirectory, sczOriginalPathRequest); + if (SUCCEEDED(hr)) + { + pMessage->request.fNetwork = TRUE; + } + } + + if (NULL == sczDirectory) + { + // Likely not a mounted drive - just copy the request then + hr = S_OK; + + hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0); + MonExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest); + } + + pMessage->handle = INVALID_HANDLE_VALUE; + pMessage->request.type = MON_DIRECTORY; + pMessage->request.fRecursive = fRecursive; + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs; + pMessage->request.hwnd = pm->hwnd; + pMessage->request.pvContext = pvDirectoryContext; + pMessage->request.sczOriginalPathRequest = sczOriginalPathRequest; + sczOriginalPathRequest = NULL; + + hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); + MonExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory); + + if (0 < pMessage->request.cPathHierarchy) + { + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + } + pMessage = NULL; + } + +LExit: + ReleaseStr(sczDirectory); + ReleaseStr(sczOriginalPathRequest); + MonAddMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonAddRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvRegKeyContext + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczSubKey = NULL; + MON_ADD_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczSubKey, wzSubKey, 0); + MonExitOnFailure(hr, "Failed to copy subkey string"); + + hr = PathBackslashTerminate(&sczSubKey); + MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL); + MonExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor"); + + pMessage->request.type = MON_REGKEY; + pMessage->request.regkey.hkRoot = hkRoot; + pMessage->request.regkey.kbKeyBitness = kbKeyBitness; + pMessage->request.fRecursive = fRecursive; + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs, + pMessage->request.hwnd = pm->hwnd; + pMessage->request.pvContext = pvRegKeyContext; + + hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); + MonExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey); + + if (0 < pMessage->request.cPathHierarchy) + { + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); + MonExitOnFailure(hr, "Failed to initiate wait"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey); + } + pMessage = NULL; + } + +LExit: + ReleaseStr(sczSubKey); + MonAddMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonRemoveDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzDirectory, + __in BOOL fRecursive + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczDirectory = NULL; + MON_REMOVE_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczDirectory, wzDirectory, 0); + MonExitOnFailure(hr, "Failed to copy directory string"); + + hr = PathBackslashTerminate(&sczDirectory); + MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->type = MON_DIRECTORY; + pMessage->fRecursive = fRecursive; + + hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0); + MonExitOnFailure(hr, "Failed to allocate copy of directory string"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + } + pMessage = NULL; + +LExit: + MonRemoveMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonRemoveRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczSubKey = NULL; + MON_REMOVE_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczSubKey, wzSubKey, 0); + MonExitOnFailure(hr, "Failed to copy subkey string"); + + hr = PathBackslashTerminate(&sczSubKey); + MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->type = MON_REGKEY; + pMessage->regkey.hkRoot = hkRoot; + pMessage->regkey.kbKeyBitness = kbKeyBitness; + pMessage->fRecursive = fRecursive; + + hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0); + MonExitOnFailure(hr, "Failed to allocate copy of directory string"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey); + } + pMessage = NULL; + +LExit: + ReleaseStr(sczSubKey); + MonRemoveMessageDestroy(pMessage); + + return hr; +} + +extern "C" void DAPI MonDestroy( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + MON_STRUCT *pm = static_cast(handle); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) + { + er = ::GetLastError(); + if (ERROR_INVALID_THREAD_ID == er) + { + // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up + er = ERROR_SUCCESS; + } + MonExitOnWin32Error(er, hr, "Failed to send message to background thread to halt"); + } + + if (pm->hCoordinatorThread) + { + ::WaitForSingleObject(pm->hCoordinatorThread, INFINITE); + ::CloseHandle(pm->hCoordinatorThread); + } + +LExit: + return; +} + +static void MonRequestDestroy( + __in MON_REQUEST *pRequest + ) +{ + if (NULL != pRequest) + { + if (MON_REGKEY == pRequest->type) + { + ReleaseRegKey(pRequest->regkey.hkSubKey); + } + else if (MON_DIRECTORY == pRequest->type && pRequest->hNotify) + { + UnregisterDeviceNotification(pRequest->hNotify); + pRequest->hNotify = NULL; + } + ReleaseStr(pRequest->sczOriginalPathRequest); + ReleaseStrArray(pRequest->rgsczPathHierarchy, pRequest->cPathHierarchy); + } +} + +static void MonAddMessageDestroy( + __in_opt MON_ADD_MESSAGE *pMessage + ) +{ + if (pMessage) + { + MonRequestDestroy(&pMessage->request); + if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle) + { + ::FindCloseChangeNotification(pMessage->handle); + } + else if (MON_REGKEY == pMessage->request.type) + { + ReleaseHandle(pMessage->handle); + } + + ReleaseMem(pMessage); + } +} + +static void MonRemoveMessageDestroy( + __in_opt MON_REMOVE_MESSAGE *pMessage + ) +{ + if (pMessage) + { + switch (pMessage->type) + { + case MON_DIRECTORY: + ReleaseStr(pMessage->directory.sczDirectory); + break; + case MON_REGKEY: + ReleaseStr(pMessage->regkey.sczSubKey); + break; + default: + Assert(false); + } + + ReleaseMem(pMessage); + } +} + +static DWORD WINAPI CoordinatorThread( + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + MSG msg = { }; + DWORD dwThreadIndex = DWORD_MAX; + DWORD dwRetries; + DWORD dwFailingNetworkWaits = 0; + MON_WAITER_CONTEXT *pWaiterContext = NULL; + MON_REMOVE_MESSAGE *pRemoveMessage = NULL; + MON_REMOVE_MESSAGE *pTempRemoveMessage = NULL; + MON_STRUCT *pm = reinterpret_cast(pvContext); + WSADATA wsaData = { }; + HANDLE hMonitor = NULL; + BOOL fRet = FALSE; + UINT_PTR uTimerSuccessfulNetworkRetry = 0; + UINT_PTR uTimerFailedNetworkRetry = 0; + + // Ensure the thread has a message queue + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + pm->fCoordinatorThreadMessageQueueInitialized = TRUE; + + hr = CreateMonWindow(pm, &pm->hwnd); + MonExitOnFailure(hr, "Failed to create window for status update thread"); + + ::WSAStartup(MAKEWORD(2, 2), &wsaData); + + hr = WaitForNetworkChanges(&hMonitor, pm); + MonExitOnFailure(hr, "Failed to wait for network changes"); + + uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL); + if (0 == uTimerSuccessfulNetworkRetry) + { + MonExitWithLastError(hr, "Failed to set timer for network successful retry"); + } + + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + MonExitOnRootFailure(hr, "Unexpected return value from message pump."); + } + else + { + switch (msg.message) + { + case MON_MESSAGE_ADD: + dwThreadIndex = DWORD_MAX; + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + if (pm->rgWaiterThreads[i].cMonitorCount < MON_MAX_MONITORS_PER_THREAD) + { + dwThreadIndex = i; + break; + } + } + + if (dwThreadIndex < pm->cWaiterThreads) + { + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; + } + else + { + hr = MemEnsureArraySize(reinterpret_cast(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH); + MonExitOnFailure(hr, "Failed to grow waiter thread array size"); + ++pm->cWaiterThreads; + + dwThreadIndex = pm->cWaiterThreads - 1; + pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE)); + MonExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct"); + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; + pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId(); + pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral; + pWaiterContext->vpfMonDirectory = pm->vpfMonDirectory; + pWaiterContext->vpfMonRegKey = pm->vpfMonRegKey; + pWaiterContext->pvContext = pm->pvContext; + + hr = MemEnsureArraySize(reinterpret_cast(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0); + MonExitOnFailure(hr, "Failed to allocate first handle"); + pWaiterContext->cHandles = 1; + + pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL); + MonExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event"); + + pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId); + if (!pWaiterContext->hWaiterThread) + { + MonExitWithLastError(hr, "Failed to create waiter thread."); + } + + dwRetries = MON_THREAD_INIT_RETRIES; + while (!pWaiterContext->fWaiterThreadMessageQueueInitialized && 0 < dwRetries) + { + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); + --dwRetries; + } + + if (0 == dwRetries) + { + hr = E_UNEXPECTED; + MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + } + } + + ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount; + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message"); + } + break; + + case MON_MESSAGE_REMOVE: + // Send remove to all waiter threads. They'll ignore it if they don't have that monitor. + // If they do have that monitor, they'll remove it from their list, and tell coordinator they have another + // empty slot via MON_MESSAGE_REMOVED message + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + pRemoveMessage = reinterpret_cast(msg.wParam); + + hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage); + MonExitOnFailure(hr, "Failed to duplicate remove message"); + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pTempRemoveMessage), msg.lParam)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + } + pTempRemoveMessage = NULL; + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message"); + } + } + MonRemoveMessageDestroy(pRemoveMessage); + pRemoveMessage = NULL; + break; + + case MON_MESSAGE_REMOVED: + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + if (pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId == static_cast(msg.wParam)) + { + Assert(pm->rgWaiterThreads[i].cMonitorCount > 0); + --pm->rgWaiterThreads[i].cMonitorCount; + if (0 == pm->rgWaiterThreads[i].cMonitorCount) + { + if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to stop"); + } + MemRemoveFromArray(reinterpret_cast(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE); + --pm->cWaiterThreads; + --i; // reprocess this index in the for loop, which will now contain the item after the one we removed + } + } + } + break; + + case MON_MESSAGE_NETWORK_WAIT_FAILED: + if (0 == dwFailingNetworkWaits) + { + uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL); + if (0 == uTimerFailedNetworkRetry) + { + MonExitWithLastError(hr, "Failed to set timer for network fail retry"); + } + } + ++dwFailingNetworkWaits; + break; + + case MON_MESSAGE_NETWORK_WAIT_SUCCEEDED: + --dwFailingNetworkWaits; + if (0 == dwFailingNetworkWaits) + { + if (!::KillTimer(NULL, uTimerFailedNetworkRetry)) + { + MonExitWithLastError(hr, "Failed to kill timer for network fail retry"); + } + uTimerFailedNetworkRetry = 0; + } + break; + + case MON_MESSAGE_NETWORK_STATUS_UPDATE: + hr = WaitForNetworkChanges(&hMonitor, pm); + MonExitOnFailure(hr, "Failed to re-wait for network changes"); + + // Propagate any network status update messages to all waiter threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + } + } + break; + + case WM_TIMER: + // Timer means some network wait is failing, and we need to retry every so often in case a remote server goes back up + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + } + } + break; + + case MON_MESSAGE_DRIVE_STATUS_UPDATE: + // If user requested to be notified of drive status updates, notify! + if (pm->vpfMonDriveStatus) + { + pm->vpfMonDriveStatus(static_cast(msg.wParam), static_cast(msg.lParam), pm->pvContext); + } + + // Propagate any drive status update messages to all waiter threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message"); + } + } + break; + + case MON_MESSAGE_STOP: + ExitFunction1(hr = static_cast(msg.wParam)); + + default: + // This thread owns a window, so this handles all the other random messages we get + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + break; + } + } + } + +LExit: + if (uTimerFailedNetworkRetry) + { + fRet = ::KillTimer(NULL, uTimerFailedNetworkRetry); + } + if (uTimerSuccessfulNetworkRetry) + { + fRet = ::KillTimer(NULL, uTimerSuccessfulNetworkRetry); + } + + if (pm->hwnd) + { + ::CloseWindow(pm->hwnd); + } + + // Tell all waiter threads to shutdown + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + if (NULL != pWaiterContext->rgHandles[0]) + { + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to waiter thread to stop"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify waiter thread of incoming message"); + } + } + } + + if (hMonitor != NULL) + { + ::WSALookupServiceEnd(hMonitor); + } + + // Now confirm they're actually shut down before returning + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + if (NULL != pWaiterContext->hWaiterThread) + { + ::WaitForSingleObject(pWaiterContext->hWaiterThread, INFINITE); + ::CloseHandle(pWaiterContext->hWaiterThread); + } + + // Waiter thread can't release these, because coordinator thread uses it to try communicating with waiter thread + ReleaseHandle(pWaiterContext->rgHandles[0]); + ReleaseMem(pWaiterContext->rgHandles); + + ReleaseMem(pWaiterContext); + } + + if (FAILED(hr)) + { + // If coordinator thread fails, notify general callback of an error + Assert(pm->vpfMonGeneral); + pm->vpfMonGeneral(hr, pm->pvContext); + } + MonRemoveMessageDestroy(pRemoveMessage); + MonRemoveMessageDestroy(pTempRemoveMessage); + + ::WSACleanup(); + + return hr; +} + +static HRESULT InitiateWait( + __inout MON_REQUEST *pRequest, + __inout HANDLE *pHandle + ) +{ + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + DEV_BROADCAST_HANDLE dev = { }; + BOOL fRedo = FALSE; + BOOL fHandleFound; + DWORD er = ERROR_SUCCESS; + DWORD dwIndex = 0; + HKEY hk = NULL; + HANDLE hTemp = INVALID_HANDLE_VALUE; + + if (pRequest->hNotify) + { + UnregisterDeviceNotification(pRequest->hNotify); + pRequest->hNotify = NULL; + } + + do + { + fRedo = FALSE; + fHandleFound = FALSE; + + for (DWORD i = 0; i < pRequest->cPathHierarchy && !fHandleFound; ++i) + { + dwIndex = pRequest->cPathHierarchy - i - 1; + switch (pRequest->type) + { + case MON_DIRECTORY: + if (INVALID_HANDLE_VALUE != *pHandle) + { + ::FindCloseChangeNotification(*pHandle); + *pHandle = INVALID_HANDLE_VALUE; + } + + *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); + if (INVALID_HANDLE_VALUE == *pHandle) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || E_ACCESSDENIED == hr) + { + continue; + } + MonExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]); + } + else + { + fHandleFound = TRUE; + hr = S_OK; + } + break; + case MON_REGKEY: + ReleaseRegKey(pRequest->regkey.hkSubKey); + hr = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex], KEY_NOTIFY | GetRegKeyBitness(pRequest), &pRequest->regkey.hkSubKey); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + continue; + } + MonExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + + er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE); + ReleaseRegKey(hk); + hr = HRESULT_FROM_WIN32(er); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || HRESULT_FROM_WIN32(ERROR_KEY_DELETED) == hr) + { + continue; + } + else + { + MonExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + + fHandleFound = TRUE; + } + + break; + default: + return E_INVALIDARG; + } + } + + pRequest->dwPathHierarchyIndex = dwIndex; + + // 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. + // If it has, restart the whole loop + if (dwIndex < pRequest->cPathHierarchy - 1) + { + switch (pRequest->type) + { + case MON_DIRECTORY: + 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); + if (INVALID_HANDLE_VALUE != hTemp) + { + ::FindCloseChangeNotification(hTemp); + fRedo = TRUE; + } + break; + case MON_REGKEY: + hrTemp = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex + 1], KEY_NOTIFY | GetRegKeyBitness(pRequest), &hk); + ReleaseRegKey(hk); + fRedo = SUCCEEDED(hrTemp); + break; + default: + Assert(false); + } + } + } while (fRedo); + + MonExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]); + + if (MON_DIRECTORY == pRequest->type) + { + dev.dbch_size = sizeof(dev); + dev.dbch_devicetype = DBT_DEVTYP_HANDLE; + dev.dbch_handle = *pHandle; + // Ignore failure on this - some drives by design don't support it (like network paths), and the worst that can happen is a + // removable device will be left in use so user cannot gracefully remove + pRequest->hNotify = RegisterDeviceNotification(pRequest->hwnd, &dev, DEVICE_NOTIFY_WINDOW_HANDLE); + } + +LExit: + ReleaseRegKey(hk); + + return hr; +} + +static DWORD WINAPI WaiterThread( + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + DWORD dwRet = 0; + BOOL fAgain = FALSE; + BOOL fContinue = TRUE; + BOOL fNotify = FALSE; + BOOL fRet = FALSE; + MSG msg = { }; + MON_ADD_MESSAGE *pAddMessage = NULL; + MON_REMOVE_MESSAGE *pRemoveMessage = NULL; + MON_WAITER_CONTEXT *pWaiterContext = reinterpret_cast(pvContext); + DWORD dwRequestIndex; + DWORD dwNewRequestIndex; + // 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) + DWORD dwWait = 0; + DWORD uCurrentTime = 0; + DWORD uLastTimeInMs = ::GetTickCount(); + DWORD uDeltaInMs = 0; + DWORD cRequestsPendingBeforeLoop = 0; + LPWSTR sczDirectory = NULL; + bool rgfProcessedIndex[MON_MAX_MONITORS_PER_THREAD + 1] = { }; + MON_INTERNAL_TEMPORARY_WAIT * pInternalWait = NULL; + + // Ensure the thread has a message queue + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + pWaiterContext->fWaiterThreadMessageQueueInitialized = TRUE; + + do + { + dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE); + + uCurrentTime = ::GetTickCount(); + uDeltaInMs = uCurrentTime - uLastTimeInMs; + uLastTimeInMs = uCurrentTime; + + if (WAIT_OBJECT_0 == dwRet) + { + do + { + fRet = ::PeekMessage(&msg, reinterpret_cast(-1), 0, 0, PM_REMOVE); + fAgain = fRet; + if (fRet) + { + switch (msg.message) + { + case MON_MESSAGE_ADD: + pAddMessage = reinterpret_cast(msg.wParam); + + // Don't just blindly put it at the end of the array - it must be before any failing requests + // for WaitForMultipleObjects() to succeed + dwNewRequestIndex = pWaiterContext->cRequests - pWaiterContext->cRequestsFailing; + if (FAILED(pAddMessage->request.hrStatus)) + { + ++pWaiterContext->cRequestsFailing; + } + + hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH); + MonExitOnFailure(hr, "Failed to insert additional handle"); + ++pWaiterContext->cHandles; + + // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL + if (MON_DIRECTORY == pAddMessage->request.type) + { + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = INVALID_HANDLE_VALUE; + } + + hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH); + MonExitOnFailure(hr, "Failed to insert additional request struct"); + ++pWaiterContext->cRequests; + + pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request; + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = pAddMessage->handle; + + ReleaseNullMem(pAddMessage); + break; + + case MON_MESSAGE_REMOVE: + pRemoveMessage = reinterpret_cast(msg.wParam); + + // Find the request to remove + hr = FindRequestIndex(pWaiterContext, pRemoveMessage, &dwRequestIndex); + if (E_NOTFOUND == hr) + { + // Coordinator sends removes blindly to all waiter threads, so maybe this one wasn't intended for us + hr = S_OK; + } + else + { + MonExitOnFailure(hr, "Failed to find request index for remove message"); + + hr = RemoveRequest(pWaiterContext, dwRequestIndex); + MonExitOnFailure(hr, "Failed to remove request after request from coordinator thread."); + } + + MonRemoveMessageDestroy(pRemoveMessage); + pRemoveMessage = NULL; + break; + + case MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, PM_NOREMOVE)) + { + // If there is another a pending retry failed wait message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && FAILED(pWaiterContext->rgRequests[i].hrStatus)) + { + // This is not a failure, just record this in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, PM_NOREMOVE)) + { + // If there is another a pending retry successful wait message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && SUCCEEDED(pWaiterContext->rgRequests[i].hrStatus)) + { + // This is not a failure, just record this in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_NETWORK_STATUS_UPDATE: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_STATUS_UPDATE, MON_MESSAGE_NETWORK_STATUS_UPDATE, PM_NOREMOVE)) + { + // If there is another a pending network status update message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork) + { + // Failures here get recorded in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_DRIVE_STATUS_UPDATE: + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].sczOriginalPathRequest[0] == static_cast(msg.wParam)) + { + // Failures here get recorded in the request's status + if (static_cast(msg.lParam)) + { + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + } + else + { + // If the message says the drive is disconnected, don't even try to wait, just mark it as gone + hrTemp = E_PATHNOTFOUND; + } + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_DRIVE_QUERY_REMOVE: + pInternalWait = reinterpret_cast(msg.wParam); + // Only do any work if message is not yet out of date + // While it could become out of date while doing this processing, sending thread will check response to guard against this + if (pInternalWait->dwSendIteration == static_cast(msg.lParam)) + { + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgHandles[i + 1] == reinterpret_cast(pInternalWait->pvContext)) + { + // Release handles ASAP so the remove request will succeed + if (pWaiterContext->rgRequests[i].hNotify) + { + UnregisterDeviceNotification(pWaiterContext->rgRequests[i].hNotify); + pWaiterContext->rgRequests[i].hNotify = NULL; + } + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); + pWaiterContext->rgHandles[i + 1] = INVALID_HANDLE_VALUE; + + // Reply to unblock our reply to the remove request + pInternalWait->dwReceiveIteration = static_cast(msg.lParam); + if (!::SetEvent(pInternalWait->hWait)) + { + 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"); + } + + // Drive is disconnecting, don't even try to wait, just mark it as gone + hrTemp = E_PATHNOTFOUND; + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + break; + } + } + } + break; + + case MON_MESSAGE_STOP: + // Stop requested, so abort the whole thread + Trace(REPORT_DEBUG, "Waiter thread was told to stop"); + fAgain = FALSE; + fContinue = FALSE; + ExitFunction1(hr = static_cast(msg.wParam)); + + default: + Assert(false); + break; + } + } + } while (fAgain); + } + else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles) + { + // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist + dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1; + fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1); + + // Initiate re-waits before we notify callback, to ensure we don't miss a single update + hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1); + hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + // 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 + if (SUCCEEDED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus) && (fNotify || (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1))) + { + Trace(REPORT_DEBUG, "Changes detected, waiting for silence period index %u", dwRequestIndex); + + if (0 < pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) + { + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs = 0; + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = TRUE; + + if (!pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) + { + pWaiterContext->rgRequests[dwRequestIndex].fPendingFire = TRUE; + ++pWaiterContext->cRequestsPending; + } + } + else + { + // If no silence period, notify immediately + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); + } + } + } + else if (WAIT_TIMEOUT != dwRet) + { + MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); + } + + // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire + // 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) + if (0 < pWaiterContext->cRequestsPending) + { + // Start at max value and find the lowest wait we can below that + dwWait = DWORD_MAX; + cRequestsPendingBeforeLoop = pWaiterContext->cRequestsPending; + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (pWaiterContext->rgRequests[i].fPendingFire) + { + if (0 == cRequestsPendingBeforeLoop) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); + MonExitOnFailure(hr, "Phantom pending fires were found!"); + } + --cRequestsPendingBeforeLoop; + + dwRequestIndex = i; + + if (pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd) + { + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = FALSE; + } + else + { + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs += uDeltaInMs; + } + + // silence period has elapsed without further notifications, so reset pending-related variables, and finally fire a notify! + if (pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs >= pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) + { + Trace(REPORT_DEBUG, "Silence period surpassed, notifying %u ms late", pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs); + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); + } + else + { + // set dwWait to the shortest interval period so that if no changes occur, WaitForMultipleObjects + // wakes the thread back up when it's time to fire the next pending notification + if (dwWait > pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs) + { + dwWait = pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs; + } + } + } + } + + // Some post-loop list validation for sanity checking + if (0 < cRequestsPendingBeforeLoop) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA); + MonExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait); + } + if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT); + MonExitOnFailure(hr, "Pending fires exist (%u), but wait was infinite", cRequestsPendingBeforeLoop); + } + } + } while (fContinue); + + // Don't bother firing pending notifications. We were told to stop monitoring, so client doesn't care. + +LExit: + ReleaseStr(sczDirectory); + MonAddMessageDestroy(pAddMessage); + MonRemoveMessageDestroy(pRemoveMessage); + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + MonRequestDestroy(pWaiterContext->rgRequests + i); + + switch (pWaiterContext->rgRequests[i].type) + { + case MON_DIRECTORY: + if (INVALID_HANDLE_VALUE != pWaiterContext->rgHandles[i + 1]) + { + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); + } + break; + case MON_REGKEY: + ReleaseHandle(pWaiterContext->rgHandles[i + 1]); + break; + default: + Assert(false); + } + } + + if (FAILED(hr)) + { + // If waiter thread fails, notify general callback of an error + Assert(pWaiterContext->vpfMonGeneral); + pWaiterContext->vpfMonGeneral(hr, pWaiterContext->pvContext); + + // And tell coordinator to shut all other waiters down + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to coordinator thread to stop (due to general failure)."); + } + } + + return hr; +} + +static void Notify( + __in HRESULT hr, + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REQUEST *pRequest + ) +{ + if (pRequest->fPendingFire) + { + --pWaiterContext->cRequestsPending; + } + + pRequest->fPendingFire = FALSE; + pRequest->fSkipDeltaAdd = FALSE; + pRequest->dwSilencePeriodInMs = 0; + + switch (pRequest->type) + { + case MON_DIRECTORY: + Assert(pWaiterContext->vpfMonDirectory); + pWaiterContext->vpfMonDirectory(hr, pRequest->sczOriginalPathRequest, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); + break; + case MON_REGKEY: + Assert(pWaiterContext->vpfMonRegKey); + pWaiterContext->vpfMonRegKey(hr, pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1], pRequest->regkey.kbKeyBitness, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); + break; + default: + Assert(false); + } +} + +static BOOL GetRecursiveFlag( + __in MON_REQUEST *pRequest, + __in DWORD dwIndex + ) +{ + if (pRequest->cPathHierarchy - 1 == dwIndex) + { + return pRequest->fRecursive; + } + else + { + return FALSE; + } +} + +static HRESULT FindRequestIndex( + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REMOVE_MESSAGE *pMessage, + __out DWORD *pdwIndex + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (pWaiterContext->rgRequests[i].type == pMessage->type) + { + switch (pWaiterContext->rgRequests[i].type) + { + case MON_DIRECTORY: + 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) + { + *pdwIndex = i; + ExitFunction1(hr = S_OK); + } + break; + case MON_REGKEY: + if (reinterpret_cast(pMessage->regkey.hkRoot) == reinterpret_cast(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) + { + *pdwIndex = i; + ExitFunction1(hr = S_OK); + } + break; + default: + Assert(false); + } + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +static HRESULT RemoveRequest( + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex + ) +{ + HRESULT hr = S_OK; + + MonRequestDestroy(pWaiterContext->rgRequests + dwRequestIndex); + + switch (pWaiterContext->rgRequests[dwRequestIndex].type) + { + case MON_DIRECTORY: + if (pWaiterContext->rgHandles[dwRequestIndex + 1] != INVALID_HANDLE_VALUE) + { + ::FindCloseChangeNotification(pWaiterContext->rgHandles[dwRequestIndex + 1]); + } + break; + case MON_REGKEY: + ReleaseHandle(pWaiterContext->rgHandles[dwRequestIndex + 1]); + break; + default: + Assert(false); + } + + if (pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) + { + --pWaiterContext->cRequestsPending; + } + + if (FAILED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus)) + { + --pWaiterContext->cRequestsFailing; + } + + MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), TRUE); + --pWaiterContext->cHandles; + MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), TRUE); + --pWaiterContext->cRequests; + + // Notify coordinator thread that a wait was removed + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast(::GetCurrentThreadId()), 0)) + { + MonExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed."); + } + +LExit: + return hr; +} + +static REGSAM GetRegKeyBitness( + __in MON_REQUEST *pRequest + ) +{ + if (REG_KEY_32BIT == pRequest->regkey.kbKeyBitness) + { + return KEY_WOW64_32KEY; + } + else if (REG_KEY_64BIT == pRequest->regkey.kbKeyBitness) + { + return KEY_WOW64_64KEY; + } + else + { + return 0; + } +} + +static HRESULT DuplicateRemoveMessage( + __in MON_REMOVE_MESSAGE *pMessage, + __out MON_REMOVE_MESSAGE **ppMessage + ) +{ + HRESULT hr = S_OK; + + *ppMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + MonExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message"); + + (*ppMessage)->type = pMessage->type; + (*ppMessage)->fRecursive = pMessage->fRecursive; + + switch (pMessage->type) + { + case MON_DIRECTORY: + hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0); + MonExitOnFailure(hr, "Failed to copy directory"); + break; + case MON_REGKEY: + (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot; + (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness; + hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0); + MonExitOnFailure(hr, "Failed to copy subkey"); + break; + default: + Assert(false); + break; + } + +LExit: + return hr; +} + +static LRESULT CALLBACK MonWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HRESULT hr = S_OK; + DEV_BROADCAST_HDR *pHdr = NULL; + DEV_BROADCAST_HANDLE *pHandle = NULL; + DEV_BROADCAST_VOLUME *pVolume = NULL; + DWORD dwUnitMask = 0; + DWORD er = ERROR_SUCCESS; + WCHAR chDrive = L'\0'; + BOOL fArrival = FALSE; + BOOL fReturnTrue = FALSE; + CREATESTRUCT *pCreateStruct = NULL; + MON_WAITER_CONTEXT *pWaiterContext = NULL; + MON_STRUCT *pm = NULL; + + // keep track of the MON_STRUCT pointer that was passed in on init, associate it with the window + if (WM_CREATE == uMsg) + { + pCreateStruct = reinterpret_cast(lParam); + if (pCreateStruct) + { + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); + } + } + else if (WM_NCDESTROY == uMsg) + { + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + } + + // Note this message ONLY comes in through WndProc, it isn't visible from the GetMessage loop. + else if (WM_DEVICECHANGE == uMsg) + { + if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam) + { + fArrival = DBT_DEVICEARRIVAL == wParam; + + pHdr = reinterpret_cast(lParam); + if (DBT_DEVTYP_VOLUME == pHdr->dbch_devicetype) + { + pVolume = reinterpret_cast(lParam); + dwUnitMask = pVolume->dbcv_unitmask; + chDrive = L'a'; + while (0 < dwUnitMask) + { + if (dwUnitMask & 0x1) + { + // This drive had a status update, so send it out to all threads + if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast(chDrive), static_cast(fArrival))) + { + MonExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE"); + } + } + dwUnitMask >>= 1; + ++chDrive; + + if (chDrive == 'z') + { + hr = E_UNEXPECTED; + MonExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask); + } + } + } + } + // We can only process device query remove messages if we have a MON_STRUCT pointer + else if (DBT_DEVICEQUERYREMOVE == wParam) + { + pm = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + if (!pm) + { + hr = E_POINTER; + MonExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored"); + } + + fReturnTrue = TRUE; + + pHdr = reinterpret_cast(lParam); + if (DBT_DEVTYP_HANDLE == pHdr->dbch_devicetype) + { + // 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 + // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread + pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL); + MonExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed"); + + pHandle = reinterpret_cast(lParam); + pm->internalWait.pvContext = pHandle->dbch_handle; + pm->internalWait.dwReceiveIteration = pm->internalWait.dwSendIteration - 1; + // This drive had a status update, so send it out to all threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast(&pm->internalWait), static_cast(pm->internalWait.dwSendIteration))) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message"); + } + } + + er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); + // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response + if (WAIT_OBJECT_0 == er) + { + // If the response ID matches what we sent, we actually got a valid reply! + if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration) + { + TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply"); + } + } + else if (WAIT_TIMEOUT == er) + { + TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message"); + } + else + { + MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); + } + ++pm->internalWait.dwSendIteration; + } + } + } + +LExit: + if (pm) + { + ReleaseHandle(pm->internalWait.hWait); + } + + if (fReturnTrue) + { + return TRUE; + } + else + { + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + } +} + +static HRESULT CreateMonWindow( + __in MON_STRUCT *pm, + __out HWND *pHwnd + ) +{ + HRESULT hr = S_OK; + WNDCLASSW wc = { }; + + wc.lpfnWndProc = MonWndProc; + wc.hInstance = ::GetModuleHandleW(NULL); + wc.lpszClassName = MONUTIL_WINDOW_CLASS; + if (!::RegisterClassW(&wc)) + { + if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError()) + { + MonExitWithLastError(hr, "Failed to register MonUtil window class."); + } + } + + *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm); + MonExitOnNullWithLastError(*pHwnd, hr, "Failed to create window."); + + // 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 + // SWP_NOACTIVATE is important so the currently active window doesn't lose focus + SetWindowPos(*pHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_DEFERERASE | SWP_NOACTIVATE); + +LExit: + return hr; +} + +static HRESULT WaitForNetworkChanges( + __inout HANDLE *phMonitor, + __in MON_STRUCT *pm + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + DWORD dwBytesReturned = 0; + WSACOMPLETION wsaCompletion = { }; + WSAQUERYSET qsRestrictions = { }; + + qsRestrictions.dwSize = sizeof(WSAQUERYSET); + qsRestrictions.dwNameSpace = NS_NLA; + + if (NULL != *phMonitor) + { + ::WSALookupServiceEnd(*phMonitor); + *phMonitor = NULL; + } + + if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor)) + { + hr = HRESULT_FROM_WIN32(::WSAGetLastError()); + MonExitOnFailure(hr, "WSALookupServiceBegin() failed"); + } + + wsaCompletion.Type = NSP_NOTIFY_HWND; + wsaCompletion.Parameters.WindowMessage.hWnd = pm->hwnd; + wsaCompletion.Parameters.WindowMessage.uMsg = MON_MESSAGE_NETWORK_STATUS_UPDATE; + nResult = ::WSANSPIoctl(*phMonitor, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &dwBytesReturned, &wsaCompletion); + if (SOCKET_ERROR != nResult || WSA_IO_PENDING != ::WSAGetLastError()) + { + hr = HRESULT_FROM_WIN32(::WSAGetLastError()); + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + MonExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError()); + } + +LExit: + return hr; +} + +static HRESULT UpdateWaitStatus( + __in HRESULT hrNewStatus, + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex, + __out_opt DWORD *pdwNewRequestIndex + ) +{ + HRESULT hr = S_OK; + DWORD dwNewRequestIndex; + MON_REQUEST *pRequest = pWaiterContext->rgRequests + dwRequestIndex; + + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwRequestIndex; + } + + if (SUCCEEDED(pRequest->hrStatus) || SUCCEEDED(hrNewStatus)) + { + // If it's a network wait, notify as long as it's new status is successful because we *may* have lost some changes + // before the wait was re-initiated. Otherwise, only notify if there was an interesting status change + if (SUCCEEDED(pRequest->hrStatus) != SUCCEEDED(hrNewStatus) || (pRequest->fNetwork && SUCCEEDED(hrNewStatus))) + { + Notify(hrNewStatus, pWaiterContext, pRequest); + } + + if (SUCCEEDED(pRequest->hrStatus) && FAILED(hrNewStatus)) + { + // If it's a network wait, notify coordinator thread that a network wait is failing + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail"); + } + + // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle + ++pWaiterContext->cRequestsFailing; + dwNewRequestIndex = pWaiterContext->cRequests - 1; + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); + // Reset pRequest to the newly swapped item + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwNewRequestIndex; + } + } + else if (FAILED(pRequest->hrStatus) && SUCCEEDED(hrNewStatus)) + { + Assert(pWaiterContext->cRequestsFailing > 0); + // If it's a network wait, notify coordinator thread that a network wait is succeeding again + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again"); + } + + --pWaiterContext->cRequestsFailing; + dwNewRequestIndex = 0; + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); + // Reset pRequest to the newly swapped item + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwNewRequestIndex; + } + } + } + + pRequest->hrStatus = hrNewStatus; + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define OsExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) +#define OsExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) +#define OsExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) +#define OsExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) +#define OsExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_OSUTIL, e, x, s, __VA_ARGS__) +#define OsExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_OSUTIL, g, x, s, __VA_ARGS__) + +typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); + +OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; +DWORD vdwOsServicePack = 0; +RTL_OSVERSIONINFOEXW vovix = { }; + +/******************************************************************** + OsGetVersion + +********************************************************************/ +extern "C" void DAPI OsGetVersion( + __out OS_VERSION* pVersion, + __out DWORD* pdwServicePack + ) +{ + OSVERSIONINFOEXW ovi = { }; + + if (OS_VERSION_UNKNOWN == vOsVersion) + { + ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + +#pragma warning (push) +#pragma warning(suppress: 4996) // deprecated +#pragma warning (push) +#pragma warning(suppress: 28159)// deprecated, use other function instead + ::GetVersionExW(reinterpret_cast(&ovi)); // only fails if version info size is set incorrectly. +#pragma warning (pop) +#pragma warning (pop) + + vdwOsServicePack = static_cast(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor; + if (4 == ovi.dwMajorVersion) + { + vOsVersion = OS_VERSION_WINNT; + } + else if (5 == ovi.dwMajorVersion) + { + if (0 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WIN2000; + } + else if (1 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WINXP; + } + else if (2 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WIN2003; + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + else if (6 == ovi.dwMajorVersion) + { + if (0 == ovi.dwMinorVersion) + { + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008; + } + else if (1 == ovi.dwMinorVersion) + { + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2; + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + + *pVersion = vOsVersion; + *pdwServicePack = vdwOsServicePack; +} + +extern "C" HRESULT DAPI OsCouldRunPrivileged( + __out BOOL* pfPrivileged + ) +{ + HRESULT hr = S_OK; + BOOL fUacEnabled = FALSE; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup = NULL; + + // Do a best effort check to see if UAC is enabled on this machine. + OsIsUacEnabled(&fUacEnabled); + + // If UAC is enabled then the process could run privileged by asking to elevate. + if (fUacEnabled) + { + *pfPrivileged = TRUE; + } + else // no UAC so only privilged if user is in administrators group. + { + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (*pfPrivileged) + { + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) + { + *pfPrivileged = FALSE; + } + } + } + + ReleaseSid(AdministratorsGroup); + return hr; +} + +extern "C" HRESULT DAPI OsIsRunningPrivileged( + __out BOOL* pfPrivileged + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + HANDLE hToken = NULL; + TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault; + DWORD dwSize = 0; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup = NULL; + + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + OsExitOnLastError(hr, "Failed to open process token."); + } + + if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) + { + *pfPrivileged = (TokenElevationTypeFull == elevationType); + ExitFunction1(hr = S_OK); + } + + // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check + er = ::GetLastError(); + if (ERROR_INVALID_FUNCTION == er) + { + er = ERROR_SUCCESS; + } + OsExitOnWin32Error(er, hr, "Failed to get process token information."); + + // Fallback to this check for some OS's (like XP) + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (*pfPrivileged) + { + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) + { + *pfPrivileged = FALSE; + } + } + +LExit: + ReleaseSid(AdministratorsGroup); + + if (hToken) + { + ::CloseHandle(hToken); + } + + return hr; +} + +extern "C" HRESULT DAPI OsIsUacEnabled( + __out BOOL* pfUacEnabled + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + DWORD dwUacEnabled = 0; + + *pfUacEnabled = FALSE; // assume UAC not enabled. + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + OsExitOnFailure(hr, "Failed to open system policy key to detect UAC."); + + hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + OsExitOnFailure(hr, "Failed to read registry value to detect UAC."); + + *pfUacEnabled = (0 != dwUacEnabled); + +LExit: + ReleaseRegKey(hk); + + return hr; +} + +HRESULT DAPI OsRtlGetVersion( + __inout RTL_OSVERSIONINFOEXW* pOvix + ) +{ + HRESULT hr = S_OK; + HMODULE hNtdll = NULL; + PFN_RTL_GET_VERSION pfnRtlGetVersion = NULL; + + if (vovix.dwOSVersionInfoSize) + { + ExitFunction(); + } + + vovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + + hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll); + if (E_MODNOTFOUND == hr) + { + OsExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); + } + OsExitOnFailure(hr, "Failed to load ntdll.dll."); + + pfnRtlGetVersion = reinterpret_cast(::GetProcAddress(hNtdll, "RtlGetVersion")); + OsExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); + + hr = static_cast(pfnRtlGetVersion(&vovix)); + +LExit: + memcpy(pOvix, &vovix, sizeof(RTL_OSVERSIONINFOEXW)); + + if (hNtdll) + { + ::FreeLibrary(hNtdll); + } + + return hr; +} 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 @@ + + + + + + + \ 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) +#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) + + +DAPI_(HRESULT) PathCanonicalizePath( + __in_z LPCWSTR wzPath, + __deref_out_z LPWSTR* psczCanonicalized + ) +{ + HRESULT hr = S_OK; + int cch = MAX_PATH + 1; + + hr = StrAlloc(psczCanonicalized, cch); + PathExitOnFailure(hr, "Failed to allocate string for the canonicalized path."); + + if (::PathCanonicalizeW(*psczCanonicalized, wzPath)) + { + hr = S_OK; + } + else + { + ExitFunctionWithLastError(hr); + } + +LExit: + return hr; +} + +DAPI_(HRESULT) PathDirectoryContainsPath( + __in_z LPCWSTR wzDirectory, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + LPWSTR sczDirectory = NULL; + LPWSTR sczOriginalPath = NULL; + LPWSTR sczOriginalDirectory = NULL; + + hr = PathCanonicalizePath(wzPath, &sczOriginalPath); + PathExitOnFailure(hr, "Failed to canonicalize the path."); + + hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory); + PathExitOnFailure(hr, "Failed to canonicalize the directory."); + + if (!sczOriginalPath || !*sczOriginalPath) + { + ExitFunction1(hr = S_FALSE); + } + if (!sczOriginalDirectory || !*sczOriginalDirectory) + { + ExitFunction1(hr = S_FALSE); + } + + sczPath = sczOriginalPath; + sczDirectory = sczOriginalDirectory; + + for (; *sczDirectory;) + { + if (!*sczPath) + { + ExitFunction1(hr = S_FALSE); + } + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1)) + { + ExitFunction1(hr = S_FALSE); + } + + ++sczDirectory; + ++sczPath; + } + + --sczDirectory; + if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath) + { + hr = S_OK; + } + else + { + hr = S_FALSE; + } + +LExit: + ReleaseStr(sczOriginalPath); + ReleaseStr(sczOriginalDirectory); + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) +#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) + +#define PATH_GOOD_ENOUGH 64 + + +DAPI_(HRESULT) PathCommandLineAppend( + __deref_inout_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ) +{ + HRESULT hr = S_OK; + LPWSTR sczQuotedArg = NULL; + BOOL fRequiresQuoting = FALSE; + DWORD dwMaxEscapedSize = 0; + + // Loop through the argument determining if it needs to be quoted and what the maximum + // size would be if there are escape characters required. + for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) + { + // Arguments with whitespace need quoting. + if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) + { + fRequiresQuoting = TRUE; + } + else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. + { + fRequiresQuoting = TRUE; + ++dwMaxEscapedSize; + } + else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. + { + ++dwMaxEscapedSize; + } + + ++dwMaxEscapedSize; + } + + // If we found anything in the argument that requires our argument to be quoted + if (fRequiresQuoting) + { + hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. + PathExitOnFailure(hr, "Failed to allocate argument to be quoted."); + + LPCWSTR pwz = wzArgument; + LPWSTR pwzQuoted = sczQuotedArg; + + *pwzQuoted = L'"'; + ++pwzQuoted; + while (*pwz) + { + DWORD dwBackslashes = 0; + while (L'\\' == *pwz) + { + ++dwBackslashes; + ++pwz; + } + + // Escape all backslashes at the end of the string. + if (!*pwz) + { + dwBackslashes *= 2; + } + else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. + { + dwBackslashes = dwBackslashes * 2 + 1; + } + // the backslashes don't have to be escaped. + + // Add the appropriate number of backslashes + for (DWORD i = 0; i < dwBackslashes; ++i) + { + *pwzQuoted = L'\\'; + ++pwzQuoted; + } + + // If there is a character, add it after all the escaped backslashes + if (*pwz) + { + *pwzQuoted = *pwz; + ++pwz; + ++pwzQuoted; + } + } + + *pwzQuoted = L'"'; + ++pwzQuoted; + *pwzQuoted = L'\0'; // ensure the arg is null terminated. + } + + // If there is already data in the command line, append a space before appending the + // argument. + if (*psczCommandLine && **psczCommandLine) + { + hr = StrAllocConcat(psczCommandLine, L" ", 0); + PathExitOnFailure(hr, "Failed to append space to command line with existing data."); + } + + hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); + PathExitOnFailure(hr, "Failed to copy command line argument."); + +LExit: + ReleaseStr(sczQuotedArg); + + return hr; +} + + +DAPI_(LPWSTR) PathFile( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + { + return NULL; + } + + LPWSTR wzFile = const_cast(wzPath); + for (LPWSTR wz = wzFile; *wz; ++wz) + { + // valid delineators + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) + { + wzFile = wz + 1; + } + } + + return wzFile; +} + + +DAPI_(LPCWSTR) PathExtension( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + { + return NULL; + } + + // Find the last dot in the last thing that could be a file. + LPCWSTR wzExtension = NULL; + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + if (L'\\' == *wz || L'/' == *wz || L':' == *wz) + { + wzExtension = NULL; + } + else if (L'.' == *wz) + { + wzExtension = wz; + } + } + + return wzExtension; +} + + +DAPI_(HRESULT) PathGetDirectory( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczDirectory + ) +{ + HRESULT hr = S_OK; + size_t cchDirectory = SIZE_T_MAX; + + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + // valid delineators: + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) + { + cchDirectory = static_cast(wz - wzPath) + 1; + } + } + + if (SIZE_T_MAX == cchDirectory) + { + // we were given just a file name, so there's no directory available + return S_FALSE; + } + + if (wzPath[0] == L'\"') + { + ++wzPath; + --cchDirectory; + } + + hr = StrAllocString(psczDirectory, wzPath, cchDirectory); + PathExitOnFailure(hr, "Failed to copy directory."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathGetParentPath( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczParent + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzParent = NULL; + + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + if (wz[1] && (L'\\' == *wz || L'/' == *wz)) + { + wzParent = wz; + } + } + + if (wzParent) + { + size_t cchPath = static_cast(wzParent - wzPath) + 1; + + hr = StrAllocString(psczParent, wzPath, cchPath); + PathExitOnFailure(hr, "Failed to copy directory."); + } + else + { + ReleaseNullStr(psczParent); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathExpand( + __out LPWSTR *psczFullPath, + __in_z LPCWSTR wzRelativePath, + __in DWORD dwResolveFlags + ) +{ + Assert(wzRelativePath && *wzRelativePath); + + HRESULT hr = S_OK; + DWORD cch = 0; + LPWSTR sczExpandedPath = NULL; + DWORD cchExpandedPath = 0; + SIZE_T cbSize = 0; + + LPWSTR sczFullPath = NULL; + + // + // First, expand any environment variables. + // + if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT) + { + cchExpandedPath = PATH_GOOD_ENOUGH; + + hr = StrAlloc(&sczExpandedPath, cchExpandedPath); + PathExitOnFailure(hr, "Failed to allocate space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + cchExpandedPath = cch; + hr = StrAlloc(&sczExpandedPath, cchExpandedPath); + PathExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + PathExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + } + } + + if (MAX_PATH < cch) + { + hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet + if (E_INVALIDARG == hr) + { + hr = S_OK; + } + PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); + + hr = StrMaxLength(sczExpandedPath, &cbSize); + PathExitOnFailure(hr, "Failed to get max length of expanded path."); + + cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize); + } + } + + // + // Second, get the full path. + // + if (dwResolveFlags & PATH_EXPAND_FULLPATH) + { + LPWSTR wzFileName = NULL; + LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; + DWORD cchFullPath = max(PATH_GOOD_ENOUGH, cchExpandedPath); + + hr = StrAlloc(&sczFullPath, cchFullPath); + PathExitOnFailure(hr, "Failed to allocate space for full path."); + + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + } + else if (cchFullPath < cch) + { + cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed + hr = StrAlloc(&sczFullPath, cchFullPath); + PathExitOnFailure(hr, "Failed to re-allocate more space for full path."); + + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + } + else if (cchFullPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + PathExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + } + } + + if (MAX_PATH < cch) + { + hr = PathPrefix(&sczFullPath); + PathExitOnFailure(hr, "Failed to prefix long path after expanding."); + } + } + else + { + sczFullPath = sczExpandedPath; + sczExpandedPath = NULL; + } + + hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); + PathExitOnFailure(hr, "Failed to copy relative path into full path."); + +LExit: + ReleaseStr(sczFullPath); + ReleaseStr(sczExpandedPath); + + return hr; +} + + +DAPI_(HRESULT) PathPrefix( + __inout LPWSTR *psczFullPath + ) +{ + Assert(psczFullPath && *psczFullPath); + + HRESULT hr = S_OK; + LPWSTR wzFullPath = *psczFullPath; + SIZE_T cbFullPath = 0; + + if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || + (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && + L':' == wzFullPath[1] && + L'\\' == wzFullPath[2]) // normal path + { + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); + PathExitOnFailure(hr, "Failed to add prefix to file path."); + } + else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC + { + // ensure that we're not already prefixed + if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) + { + hr = StrSize(*psczFullPath, &cbFullPath); + PathExitOnFailure(hr, "Failed to get size of full path."); + + memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); + + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); + PathExitOnFailure(hr, "Failed to add prefix to UNC path."); + } + } + else + { + hr = E_INVALIDARG; + PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathFixedBackslashTerminate( + __inout_ecount_z(cchPath) LPWSTR wzPath, + __in SIZE_T cchPath + ) +{ + HRESULT hr = S_OK; + size_t cchLength = 0; + + hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); + PathExitOnFailure(hr, "Failed to get length of path."); + + if (cchLength >= cchPath) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else if (L'\\' != wzPath[cchLength - 1]) + { + wzPath[cchLength] = L'\\'; + wzPath[cchLength + 1] = L'\0'; + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathBackslashTerminate( + __inout LPWSTR* psczPath + ) +{ + Assert(psczPath && *psczPath); + + HRESULT hr = S_OK; + SIZE_T cchPath = 0; + size_t cchLength = 0; + + hr = StrMaxLength(*psczPath, &cchPath); + PathExitOnFailure(hr, "Failed to get size of path string."); + + hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); + PathExitOnFailure(hr, "Failed to get length of path."); + + if (L'\\' != (*psczPath)[cchLength - 1]) + { + hr = StrAllocConcat(psczPath, L"\\", 1); + PathExitOnFailure(hr, "Failed to concat backslash onto string."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathForCurrentProcess( + __inout LPWSTR *psczFullPath, + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + DWORD cch = MAX_PATH; + + do + { + hr = StrAlloc(psczFullPath, cch); + PathExitOnFailure(hr, "Failed to allocate string for module path."); + + DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); + if (0 == cchRequired) + { + PathExitWithLastError(hr, "Failed to get path for executing process."); + } + else if (cchRequired == cch) + { + cch = cchRequired + 1; + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else + { + hr = S_OK; + } + } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathRelativeToModule( + __inout LPWSTR *psczFullPath, + __in_opt LPCWSTR wzFileName, + __in_opt HMODULE hModule + ) +{ + HRESULT hr = PathForCurrentProcess(psczFullPath, hModule); + PathExitOnFailure(hr, "Failed to get current module path."); + + hr = PathGetDirectory(*psczFullPath, psczFullPath); + PathExitOnFailure(hr, "Failed to get current module directory."); + + if (wzFileName) + { + hr = PathConcat(*psczFullPath, wzFileName, psczFullPath); + PathExitOnFailure(hr, "Failed to append filename."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathCreateTempFile( + __in_opt LPCWSTR wzDirectory, + __in_opt __format_string LPCWSTR wzFileNameTemplate, + __in DWORD dwUniqueCount, + __in DWORD dwFileAttributes, + __out_opt LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ) +{ + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); + + HRESULT hr = S_OK; + + LPWSTR sczTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + LPWSTR scz = NULL; + LPWSTR sczTempFile = NULL; + + if (wzDirectory && *wzDirectory) + { + hr = StrAllocString(&sczTempPath, wzDirectory, 0); + PathExitOnFailure(hr, "Failed to copy temp path."); + } + else + { + hr = StrAlloc(&sczTempPath, cchTempPath); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); + + if (!::GetTempPathW(cchTempPath, sczTempPath)) + { + PathExitWithLastError(hr, "Failed to get temp path."); + } + } + + if (wzFileNameTemplate && *wzFileNameTemplate) + { + for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); + PathExitOnFailure(hr, "Failed to allocate memory for file template."); + + hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); + PathExitOnFailure(hr, "Failed to allocate temp file name."); + + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + } + PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); + } + } + } + + // If we were not able to or we did not try to create a temp file, ask + // the system to provide us a temp file using its built-in mechanism. + if (INVALID_HANDLE_VALUE == hTempFile) + { + hr = StrAlloc(&sczTempFile, MAX_PATH); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); + + if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) + { + PathExitWithLastError(hr, "Failed to create new temp file name."); + } + + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + PathExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); + } + } + + // If the caller wanted the temp file name or handle, return them here. + if (psczTempFile) + { + hr = StrAllocString(psczTempFile, sczTempFile, 0); + PathExitOnFailure(hr, "Failed to copy temp file string."); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + if (INVALID_HANDLE_VALUE != hTempFile) + { + ::CloseHandle(hTempFile); + } + + ReleaseStr(scz); + ReleaseStr(sczTempFile); + ReleaseStr(sczTempPath); + + return hr; +} + + +DAPI_(HRESULT) PathCreateTimeBasedTempFile( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzPrefix, + __in_z_opt LPCWSTR wzPostfix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ) +{ + HRESULT hr = S_OK; + BOOL fRetry = FALSE; + WCHAR wzTempPath[MAX_PATH] = { }; + LPWSTR sczPrefix = NULL; + LPWSTR sczPrefixFolder = NULL; + SYSTEMTIME time = { }; + + LPWSTR sczTempPath = NULL; + HANDLE hTempFile = INVALID_HANDLE_VALUE; + DWORD dwAttempts = 0; + + if (wzDirectory && *wzDirectory) + { + hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix); + PathExitOnFailure(hr, "Failed to combine directory and log prefix."); + } + else + { + if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + PathExitWithLastError(hr, "Failed to get temp folder."); + } + + hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); + PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); + } + + hr = PathGetDirectory(sczPrefix, &sczPrefixFolder); + if (S_OK == hr) + { + hr = DirEnsureExists(sczPrefixFolder, NULL); + PathExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); + } + + if (!wzPostfix) + { + wzPostfix = L""; + } + + do + { + fRetry = FALSE; + ++dwAttempts; + + ::GetLocalTime(&time); + + // Log format: pre YYYY MM dd hh mm ss post ext + 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); + PathExitOnFailure(hr, "failed to allocate memory for the temp path"); + + hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // If the file already exists, just try again. + DWORD er = ::GetLastError(); + if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er) + { + ::Sleep(100); + + if (10 > dwAttempts) + { + er = ERROR_SUCCESS; + fRetry = TRUE; + } + } + + hr = HRESULT_FROM_WIN32(er); + PathExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); + } + } while (fRetry); + + if (psczTempFile) + { + hr = StrAllocString(psczTempFile, sczTempPath, 0); + PathExitOnFailure(hr, "Failed to copy temp path to return."); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(sczTempPath); + ReleaseStr(sczPrefixFolder); + ReleaseStr(sczPrefix); + + return hr; +} + + +DAPI_(HRESULT) PathCreateTempDirectory( + __in_opt LPCWSTR wzDirectory, + __in __format_string LPCWSTR wzDirectoryNameTemplate, + __in DWORD dwUniqueCount, + __out LPWSTR* psczTempDirectory + ) +{ + AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified."); + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); + + HRESULT hr = S_OK; + + LPWSTR sczTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + LPWSTR scz = NULL; + + if (wzDirectory && *wzDirectory) + { + hr = StrAllocString(&sczTempPath, wzDirectory, 0); + PathExitOnFailure(hr, "Failed to copy temp path."); + + hr = PathBackslashTerminate(&sczTempPath); + PathExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); + } + else + { + hr = StrAlloc(&sczTempPath, cchTempPath); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); + + if (!::GetTempPathW(cchTempPath, sczTempPath)) + { + PathExitWithLastError(hr, "Failed to get temp path."); + } + } + + for (DWORD i = 1; i <= dwUniqueCount; ++i) + { + hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i); + PathExitOnFailure(hr, "Failed to allocate memory for directory name template."); + + hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz); + PathExitOnFailure(hr, "Failed to allocate temp directory name."); + + if (!::CreateDirectoryW(*psczTempDirectory, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + continue; + } + else if (ERROR_PATH_NOT_FOUND == er) + { + hr = DirEnsureExists(*psczTempDirectory, NULL); + break; + } + else + { + hr = HRESULT_FROM_WIN32(er); + break; + } + } + else + { + hr = S_OK; + break; + } + } + PathExitOnFailure(hr, "Failed to create temp directory."); + + hr = PathBackslashTerminate(psczTempDirectory); + PathExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); + +LExit: + ReleaseStr(scz); + ReleaseStr(sczTempPath); + + return hr; +} + + +DAPI_(HRESULT) PathGetKnownFolder( + __in int csidl, + __out LPWSTR* psczKnownFolder + ) +{ + HRESULT hr = S_OK; + + hr = StrAlloc(psczKnownFolder, MAX_PATH); + PathExitOnFailure(hr, "Failed to allocate memory for known folder."); + + hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); + PathExitOnFailure(hr, "Failed to get known folder path."); + + hr = PathBackslashTerminate(psczKnownFolder); + PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); + +LExit: + return hr; +} + + +DAPI_(BOOL) PathIsAbsolute( + __in_z LPCWSTR wzPath + ) +{ + return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); +} + + +DAPI_(HRESULT) PathConcat( + __in_opt LPCWSTR wzPath1, + __in_opt LPCWSTR wzPath2, + __deref_out_z LPWSTR* psczCombined + ) +{ + return PathConcatCch(wzPath1, 0, wzPath2, 0, psczCombined); +} + + +DAPI_(HRESULT) PathConcatCch( + __in_opt LPCWSTR wzPath1, + __in SIZE_T cchPath1, + __in_opt LPCWSTR wzPath2, + __in SIZE_T cchPath2, + __deref_out_z LPWSTR* psczCombined + ) +{ + HRESULT hr = S_OK; + + if (!wzPath2 || !*wzPath2) + { + hr = StrAllocString(psczCombined, wzPath1, cchPath1); + PathExitOnFailure(hr, "Failed to copy just path1 to output."); + } + else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) + { + hr = StrAllocString(psczCombined, wzPath2, cchPath2); + PathExitOnFailure(hr, "Failed to copy just path2 to output."); + } + else + { + hr = StrAllocString(psczCombined, wzPath1, cchPath1); + PathExitOnFailure(hr, "Failed to copy path1 to output."); + + hr = PathBackslashTerminate(psczCombined); + PathExitOnFailure(hr, "Failed to backslashify."); + + hr = StrAllocConcat(psczCombined, wzPath2, cchPath2); + PathExitOnFailure(hr, "Failed to append path2 to output."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathEnsureQuoted( + __inout LPWSTR* ppszPath, + __in BOOL fDirectory + ) +{ + Assert(ppszPath && *ppszPath); + + HRESULT hr = S_OK; + size_t cchPath = 0; + + hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath); + PathExitOnFailure(hr, "Failed to get the length of the path."); + + // Handle simple special cases. + if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0])) + { + hr = StrAllocString(ppszPath, L"\"\"", 2); + PathExitOnFailure(hr, "Failed to allocate a quoted empty string."); + + ExitFunction(); + } + + if (L'"' != (*ppszPath)[0]) + { + hr = StrAllocPrefix(ppszPath, L"\"", 1); + PathExitOnFailure(hr, "Failed to allocate an opening quote."); + + // Add a char for the opening quote. + ++cchPath; + } + + if (L'"' != (*ppszPath)[cchPath - 1]) + { + hr = StrAllocConcat(ppszPath, L"\"", 1); + PathExitOnFailure(hr, "Failed to allocate a closing quote."); + + // Add a char for the closing quote. + ++cchPath; + } + + if (fDirectory) + { + if (L'\\' != (*ppszPath)[cchPath - 2]) + { + // Change the last char to a backslash and re-append the closing quote. + (*ppszPath)[cchPath - 1] = L'\\'; + + hr = StrAllocConcat(ppszPath, L"\"", 1); + PathExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); + } + } + +LExit: + + return hr; +} + + +DAPI_(HRESULT) PathCompare( + __in_z LPCWSTR wzPath1, + __in_z LPCWSTR wzPath2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath1 = NULL; + LPWSTR sczPath2 = NULL; + + hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + PathExitOnFailure(hr, "Failed to expand path1."); + + hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + PathExitOnFailure(hr, "Failed to expand path2."); + + *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); + +LExit: + ReleaseStr(sczPath2); + ReleaseStr(sczPath1); + + return hr; +} + + +DAPI_(HRESULT) PathCompress( + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + HANDLE hPath = INVALID_HANDLE_VALUE; + + hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (INVALID_HANDLE_VALUE == hPath) + { + PathExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); + } + + DWORD dwBytesReturned = 0; + USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT; + if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL)) + { + // ignore compression attempts on file systems that don't support it + DWORD er = ::GetLastError(); + if (ERROR_INVALID_FUNCTION != er) + { + PathExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); + } + } + +LExit: + ReleaseFile(hPath); + + return hr; +} + +DAPI_(HRESULT) PathGetHierarchyArray( + __in_z LPCWSTR wzPath, + __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, + __inout LPUINT pcPathArray + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPathCopy = NULL; + LPWSTR sczNewPathCopy = NULL; + DWORD cArraySpacesNeeded = 0; + size_t cchPath = 0; + + hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); + PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath); + + if (!cchPath) + { + ExitFunction1(hr = E_INVALIDARG); + } + + for (size_t i = 0; i < cchPath; ++i) + { + if (wzPath[i] == L'\\') + { + ++cArraySpacesNeeded; + } + } + + if (wzPath[cchPath - 1] != L'\\') + { + ++cArraySpacesNeeded; + } + + // 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. + if (wzPath[0] == L'\\' && wzPath[1] == L'\\') + { + cArraySpacesNeeded -= 3; + } + + Assert(cArraySpacesNeeded >= 1); + + hr = MemEnsureArraySize(reinterpret_cast(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); + PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); + *pcPathArray = cArraySpacesNeeded; + + hr = StrAllocString(&sczPathCopy, wzPath, 0); + PathExitOnFailure(hr, "Failed to allocate copy of original path"); + + for (DWORD i = 0; i < cArraySpacesNeeded; ++i) + { + hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); + PathExitOnFailure(hr, "Failed to copy path"); + + DWORD cchPathCopy = lstrlenW(sczPathCopy); + + // 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 + if (wzPath[cchPathCopy - 1] == L'\\') + { + sczPathCopy[cchPathCopy - 1] = L'\0'; + } + + hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); + PathExitOnFailure(hr, "Failed to get directory portion of path"); + + ReleaseStr(sczPathCopy); + sczPathCopy = sczNewPathCopy; + sczNewPathCopy = NULL; + } + + hr = S_OK; + +LExit: + ReleaseStr(sczPathCopy); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PerfExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) +#define PerfExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) +#define PerfExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) +#define PerfExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) +#define PerfExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PERFUTIL, e, x, s, __VA_ARGS__) +#define PerfExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PERFUTIL, g, x, s, __VA_ARGS__) + +static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter +static double vdFrequency = 1; + + +/******************************************************************** + PerfInitialize - initializes internal static variables + +********************************************************************/ +extern "C" void DAPI PerfInitialize( + ) +{ + LARGE_INTEGER liFrequency = { }; + + // + // check for high perf counter + // + if (!::QueryPerformanceFrequency(&liFrequency)) + { + vfHighPerformanceCounter = FALSE; + vdFrequency = 1000; // ticks are measured in milliseconds + } + else + vdFrequency = static_cast(liFrequency.QuadPart); +} + + +/******************************************************************** + PerfClickTime - resets the clicker, or returns elapsed time since last call + + NOTE: if pliElapsed is NULL, resets the elapsed time + if pliElapsed is not NULL, returns perf number since last call to PerfClickTime() +********************************************************************/ +extern "C" void DAPI PerfClickTime( + __out_opt LARGE_INTEGER* pliElapsed + ) +{ + static LARGE_INTEGER liStart = { }; + LARGE_INTEGER* pli = pliElapsed; + + if (!pli) // if elapsed time time was not requested, reset the start time + pli = &liStart; + + if (vfHighPerformanceCounter) + ::QueryPerformanceCounter(pli); + else + pli->QuadPart = ::GetTickCount(); + + if (pliElapsed) + pliElapsed->QuadPart -= liStart.QuadPart; +} + + +/******************************************************************** + PerfConvertToSeconds - converts perf number to seconds + +********************************************************************/ +extern "C" double DAPI PerfConvertToSeconds( + __in const LARGE_INTEGER* pli + ) +{ + Assert(0 < vdFrequency); + return pli->QuadPart / vdFrequency; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PolcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) +#define PolcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) +#define PolcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) +#define PolcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) +#define PolcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_POLCUTIL, e, x, s, __VA_ARGS__) +#define PolcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_POLCUTIL, g, x, s, __VA_ARGS__) + +const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\"; + +static HRESULT OpenPolicyKey( + __in_z LPCWSTR wzPolicyPath, + __out HKEY* phk + ); + + +extern "C" HRESULT DAPI PolcReadNumber( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in DWORD dwDefault, + __out DWORD* pdw + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + + hr = OpenPolicyKey(wzPolicyPath, &hk); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + + hr = RegReadNumber(hk, wzPolicyName, pdw); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + +LExit: + ReleaseRegKey(hk); + + if (S_FALSE == hr || FAILED(hr)) + { + *pdw = dwDefault; + } + + return hr; +} + +extern "C" HRESULT DAPI PolcReadString( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in_z_opt LPCWSTR wzDefault, + __deref_out_z LPWSTR* pscz + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + + hr = OpenPolicyKey(wzPolicyPath, &hk); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + + hr = RegReadString(hk, wzPolicyName, pscz); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + +LExit: + ReleaseRegKey(hk); + + if (S_FALSE == hr || FAILED(hr)) + { + if (NULL == wzDefault) + { + ReleaseNullStr(*pscz); + } + else + { + hr = StrAllocString(pscz, wzDefault, 0); + } + } + + return hr; +} + + +// internal functions + +static HRESULT OpenPolicyKey( + __in_z LPCWSTR wzPolicyPath, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath); + PolcExitOnFailure(hr, "Failed to combine logging path with root path."); + + hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk); + PolcExitOnFailure(hr, "Failed to open policy registry key."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#ifndef _WIN32_MSI +#define _WIN32_MSI 200 +#endif + +#define JET_VERSION 0x0501 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dutilsources.h" +#include "dutil.h" +#include "verutil.h" +#include "aclutil.h" +#include "atomutil.h" +#include "buffutil.h" +#include "butil.h" +#include "cabcutil.h" +#include "cabutil.h" +#include "conutil.h" +#include "cryputil.h" +#include "eseutil.h" +#include "dirutil.h" +#include "dlutil.h" +#include "dpiutil.h" +#include "fileutil.h" +#include "guidutil.h" +#include "gdiputil.h" +#include "dictutil.h" +#include "deputil.h" // NOTE: This must come after dictutil.h since it uses it. +#include "inetutil.h" +#include "iniutil.h" +#include "jsonutil.h" +#include "locutil.h" +#include "logutil.h" +#include "memutil.h" // NOTE: almost everying is inlined so there is a small .cpp file +//#include "metautil.h" - see metautil.cpp why this *must* be commented out +#include "monutil.h" +#include "osutil.h" +#include "pathutil.h" +#include "perfutil.h" +#include "polcutil.h" +#include "procutil.h" +#include "regutil.h" +#include "resrutil.h" +#include "reswutil.h" +#include "rmutil.h" +#include "rssutil.h" +#include "apuputil.h" // NOTE: this must come after atomutil.h and rssutil.h since it uses them. +#include "shelutil.h" +//#include "sqlutil.h" - see sqlutil.cpp why this *must* be commented out +#include "srputil.h" +#include "strutil.h" +#include "timeutil.h" +#include "timeutil.h" +#include "thmutil.h" +#include "uncutil.h" +#include "uriutil.h" +#include "userutil.h" +#include "wiutil.h" +#include "wuautil.h" +#include // This header is needed for msxml2.h to compile correctly +#include // This file is needed to include xmlutil.h +#include "xmlutil.h" + 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** + ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcFindAllIdsFromExeName( + __in_z LPCWSTR wzExeName, + __out DWORD** ppdwProcessIds, + __out DWORD* pcProcessIds + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HANDLE hSnap = INVALID_HANDLE_VALUE; + BOOL fContinue = FALSE; + PROCESSENTRY32W peData = { sizeof(peData) }; + + hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (INVALID_HANDLE_VALUE == hSnap) + { + ProcExitWithLastError(hr, "Failed to create snapshot of processes on system"); + } + + fContinue = ::Process32FirstW(hSnap, &peData); + + while (fContinue) + { + if (0 == lstrcmpiW((LPCWSTR)&(peData.szExeFile), wzExeName)) + { + if (!*ppdwProcessIds) + { + *ppdwProcessIds = static_cast(MemAlloc(sizeof(DWORD), TRUE)); + ProcExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs."); + } + else + { + DWORD* pdwReAllocReturnedPids = NULL; + pdwReAllocReturnedPids = static_cast(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE)); + ProcExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs."); + + *ppdwProcessIds = pdwReAllocReturnedPids; + } + + (*ppdwProcessIds)[*pcProcessIds] = peData.th32ProcessID; + ++(*pcProcessIds); + } + + fContinue = ::Process32NextW(hSnap, &peData); + } + + er = ::GetLastError(); + if (ERROR_NO_MORE_FILES == er) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + ReleaseFile(hSnap); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + +static HRESULT GetActiveSessionUserToken( + __out HANDLE *phToken + ); + + +/******************************************************************** + ProcExecuteAsInteractiveUser() - runs process as currently logged in + user. +*******************************************************************/ +extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser( + __in_z LPCWSTR wzExecutablePath, + __in_z LPCWSTR wzCommandLine, + __out HANDLE *phProcess + ) +{ + HRESULT hr = S_OK; + HANDLE hToken = NULL; + LPVOID pEnvironment = NULL; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + hr = GetActiveSessionUserToken(&hToken); + ProcExitOnFailure(hr, "Failed to get active session user token."); + + if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE)) + { + ProcExitWithLastError(hr, "Failed to create environment block for UI process."); + } + + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine); + ProcExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi)) + { + ProcExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine); + } + + *phProcess = pi.hProcess; + pi.hProcess = NULL; + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseStr(sczFullCommandLine); + + if (pEnvironment) + { + ::DestroyEnvironmentBlock(pEnvironment); + } + + ReleaseHandle(hToken); + + return hr; +} + + +static HRESULT GetActiveSessionUserToken( + __out HANDLE *phToken + ) +{ + HRESULT hr = S_OK; + PWTS_SESSION_INFO pSessionInfo = NULL; + DWORD cSessions = 0; + DWORD dwSessionId = 0; + BOOL fSessionFound = FALSE; + HANDLE hToken = NULL; + + // Loop through the sessions looking for the active one. + if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions)) + { + ProcExitWithLastError(hr, "Failed to enumerate sessions."); + } + + for (DWORD i = 0; i < cSessions; ++i) + { + if (WTSActive == pSessionInfo[i].State) + { + dwSessionId = pSessionInfo[i].SessionId; + fSessionFound = TRUE; + + break; + } + } + + if (!fSessionFound) + { + ExitFunction1(hr = E_NOTFOUND); + } + + // Get the user token from the active session. + if (!::WTSQueryUserToken(dwSessionId, &hToken)) + { + ProcExitWithLastError(hr, "Failed to get active session user token."); + } + + *phToken = hToken; + hToken = NULL; + +LExit: + ReleaseHandle(hToken); + + if (pSessionInfo) + { + ::WTSFreeMemory(pSessionInfo); + } + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + + +// private functions +static HRESULT CreatePipes( + __out HANDLE *phOutRead, + __out HANDLE *phOutWrite, + __out HANDLE *phErrWrite, + __out HANDLE *phInRead, + __out HANDLE *phInWrite + ); + +static BOOL CALLBACK CloseWindowEnumCallback( + __in HWND hWnd, + __in LPARAM lParam + ); + + +extern "C" HRESULT DAPI ProcElevated( + __in HANDLE hProcess, + __out BOOL* pfElevated + ) +{ + HRESULT hr = S_OK; + HANDLE hToken = NULL; + TOKEN_ELEVATION tokenElevated = { }; + DWORD cbToken = 0; + + if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + { + ProcExitWithLastError(hr, "Failed to open process token."); + } + + if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) + { + *pfElevated = (0 != tokenElevated.TokenIsElevated); + } + else + { + DWORD er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + + // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated. + if (E_INVALIDARG == hr) + { + *pfElevated = FALSE; + hr = S_OK; + } + else + { + ProcExitOnRootFailure(hr, "Failed to get elevation token from process."); + } + } + +LExit: + ReleaseHandle(hToken); + + return hr; +} + +extern "C" HRESULT DAPI ProcWow64( + __in HANDLE hProcess, + __out BOOL* pfWow64 + ) +{ + HRESULT hr = S_OK; + BOOL fIsWow64 = FALSE; + + typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); + LPFN_ISWOW64PROCESS2 pfnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process2"); + + if (pfnIsWow64Process2) + { + USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN; + if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr)) + { + ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2."); + } + + if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN) + { + fIsWow64 = TRUE; + } + } + else + { + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process"); + + if (pfnIsWow64Process) + { + if (!pfnIsWow64Process(hProcess, &fIsWow64)) + { + ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process."); + } + } + } + + *pfWow64 = fIsWow64; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ) +{ + AssertSz(!pfsr->fDisabled, "File system redirection was already disabled."); + HRESULT hr = S_OK; + + typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *); + LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); + + if (!pfnWow64DisableWow64FsRedirection) + { + ExitFunction1(hr = E_NOTIMPL); + } + + if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState)) + { + ProcExitWithLastError(hr, "Failed to disable file system redirection."); + } + + pfsr->fDisabled = TRUE; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ) +{ + HRESULT hr = S_OK; + + if (pfsr->fDisabled) + { + typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID); + LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); + + if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState)) + { + ProcExitWithLastError(hr, "Failed to revert file system redirection."); + } + + pfsr->fDisabled = FALSE; + pfsr->pvRevertState = NULL; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI ProcExec( + __in_z LPCWSTR wzExecutablePath, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out HANDLE *phProcess + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L""); + ProcExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + si.wShowWindow = static_cast(nCmdShow); + if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) + { + ProcExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine); + } + + *phProcess = pi.hProcess; + pi.hProcess = NULL; + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseStr(sczFullCommandLine); + + return hr; +} + + +/******************************************************************** + ProcExecute() - executes a command-line. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcExecute( + __in_z LPWSTR wzCommand, + __out HANDLE *phProcess, + __out_opt HANDLE *phChildStdIn, + __out_opt HANDLE *phChildStdOutErr + ) +{ + HRESULT hr = S_OK; + + PROCESS_INFORMATION pi = { }; + STARTUPINFOW si = { }; + + HANDLE hOutRead = INVALID_HANDLE_VALUE; + HANDLE hOutWrite = INVALID_HANDLE_VALUE; + HANDLE hErrWrite = INVALID_HANDLE_VALUE; + HANDLE hInRead = INVALID_HANDLE_VALUE; + HANDLE hInWrite = INVALID_HANDLE_VALUE; + + // Create redirect pipes. + hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); + ProcExitOnFailure(hr, "failed to create output pipes"); + + // Set up startup structure. + si.cb = sizeof(STARTUPINFOW); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = hInRead; + si.hStdOutput = hOutWrite; + si.hStdError = hErrWrite; + +#pragma prefast(push) +#pragma prefast(disable:25028) + if (::CreateProcessW(NULL, + wzCommand, // command line + NULL, // security info + NULL, // thread info + TRUE, // inherit handles + ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags + NULL, // environment + NULL, // cur dir + &si, + &pi)) +#pragma prefast(pop) + { + // Close child process output/input handles so child doesn't hang + // while waiting for input from parent process. + ::CloseHandle(hOutWrite); + hOutWrite = INVALID_HANDLE_VALUE; + + ::CloseHandle(hErrWrite); + hErrWrite = INVALID_HANDLE_VALUE; + + ::CloseHandle(hInRead); + hInRead = INVALID_HANDLE_VALUE; + } + else + { + ProcExitWithLastError(hr, "Process failed to execute."); + } + + *phProcess = pi.hProcess; + pi.hProcess = 0; + + if (phChildStdIn) + { + *phChildStdIn = hInWrite; + hInWrite = INVALID_HANDLE_VALUE; + } + + if (phChildStdOutErr) + { + *phChildStdOutErr = hOutRead; + hOutRead = INVALID_HANDLE_VALUE; + } + +LExit: + if (pi.hThread) + { + ::CloseHandle(pi.hThread); + } + + if (pi.hProcess) + { + ::CloseHandle(pi.hProcess); + } + + ReleaseFileHandle(hOutRead); + ReleaseFileHandle(hOutWrite); + ReleaseFileHandle(hErrWrite); + ReleaseFileHandle(hInRead); + ReleaseFileHandle(hInWrite); + + return hr; +} + + +/******************************************************************** + ProcWaitForCompletion() - waits for process to complete and gets return code. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcWaitForCompletion( + __in HANDLE hProcess, + __in DWORD dwTimeout, + __out DWORD *pReturnCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + // Wait for everything to finish + er = ::WaitForSingleObject(hProcess, dwTimeout); + if (WAIT_FAILED == er) + { + ProcExitWithLastError(hr, "Failed to wait for process to complete."); + } + else if (WAIT_TIMEOUT == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + + if (!::GetExitCodeProcess(hProcess, &er)) + { + ProcExitWithLastError(hr, "Failed to get process return code."); + } + + *pReturnCode = er; + +LExit: + return hr; +} + +/******************************************************************** + ProcWaitForIds() - waits for multiple processes to complete. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcWaitForIds( + __in_ecount(cProcessIds) const DWORD rgdwProcessIds[], + __in DWORD cProcessIds, + __in DWORD dwMilliseconds + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HANDLE hProcess = NULL; + HANDLE * rghProcesses = NULL; + DWORD cProcesses = 0; + + rghProcesses = static_cast(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); + ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); + + for (DWORD i = 0; i < cProcessIds; ++i) + { + hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]); + if (hProcess != NULL) + { + rghProcesses[cProcesses++] = hProcess; + } + } + + er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); + if (WAIT_FAILED == er) + { + ProcExitWithLastError(hr, "Failed to wait for process to complete."); + } + else if (WAIT_TIMEOUT == er) + { + ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); + } + +LExit: + if (rghProcesses) + { + for (DWORD i = 0; i < cProcesses; ++i) + { + if (NULL != rghProcesses[i]) + { + ::CloseHandle(rghProcesses[i]); + } + } + + MemFree(rghProcesses); + } + + return hr; +} + +/******************************************************************** + ProcCloseIds() - sends WM_CLOSE messages to all process ids. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcCloseIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cProcessIds; ++i) + { + if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i])) + { + ProcExitWithLastError(hr, "Failed to enumerate windows."); + } + } + +LExit: + return hr; +} + + +static HRESULT CreatePipes( + __out HANDLE *phOutRead, + __out HANDLE *phOutWrite, + __out HANDLE *phErrWrite, + __out HANDLE *phInRead, + __out HANDLE *phInWrite + ) +{ + HRESULT hr = S_OK; + SECURITY_ATTRIBUTES sa; + HANDLE hOutTemp = INVALID_HANDLE_VALUE; + HANDLE hInTemp = INVALID_HANDLE_VALUE; + + HANDLE hOutRead = INVALID_HANDLE_VALUE; + HANDLE hOutWrite = INVALID_HANDLE_VALUE; + HANDLE hErrWrite = INVALID_HANDLE_VALUE; + HANDLE hInRead = INVALID_HANDLE_VALUE; + HANDLE hInWrite = INVALID_HANDLE_VALUE; + + // Fill out security structure so we can inherit handles + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + // Create pipes + if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) + { + ProcExitWithLastError(hr, "failed to create output pipe"); + } + + if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) + { + ProcExitWithLastError(hr, "failed to create input pipe"); + } + + // Duplicate output pipe so standard error and standard output write to the same pipe. + if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + ProcExitWithLastError(hr, "failed to duplicate write handle"); + } + + // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in + // the child process that can't be closed. + if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ProcExitWithLastError(hr, "failed to duplicate output pipe"); + } + + if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ProcExitWithLastError(hr, "failed to duplicate input pipe"); + } + + // now that everything has succeeded, assign to the outputs + *phOutRead = hOutRead; + hOutRead = INVALID_HANDLE_VALUE; + + *phOutWrite = hOutWrite; + hOutWrite = INVALID_HANDLE_VALUE; + + *phErrWrite = hErrWrite; + hErrWrite = INVALID_HANDLE_VALUE; + + *phInRead = hInRead; + hInRead = INVALID_HANDLE_VALUE; + + *phInWrite = hInWrite; + hInWrite = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hOutRead); + ReleaseFileHandle(hOutWrite); + ReleaseFileHandle(hErrWrite); + ReleaseFileHandle(hInRead); + ReleaseFileHandle(hInWrite); + ReleaseFileHandle(hOutTemp); + ReleaseFileHandle(hInTemp); + + return hr; +} + + +/******************************************************************** + CloseWindowEnumCallback() - outputs trace and log info + +*******************************************************************/ +static BOOL CALLBACK CloseWindowEnumCallback( + __in HWND hWnd, + __in LPARAM lParam + ) +{ + DWORD dwPid = static_cast(lParam); + DWORD dwProcessId = 0; + + ::GetWindowThreadProcessId(hWnd, &dwProcessId); + if (dwPid == dwProcessId) + { + ::SendMessageW(hWnd, WM_CLOSE, 0, 0); + } + + return TRUE; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define RegExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) +#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) +#define RegExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) +#define RegExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) +#define RegExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REGUTIL, e, x, s, __VA_ARGS__) +#define RegExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REGUTIL, g, x, s, __VA_ARGS__) + +static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW; +static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW; +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL; +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL; +static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW; +static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW; +static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW; +static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW; +static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; +static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; +static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; + +static HMODULE vhAdvApi32Dll = NULL; +static BOOL vfRegInitialized = FALSE; + +static HRESULT WriteStringToRegistry( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue, + __in DWORD dwType +); + +/******************************************************************** + RegInitialize - initializes regutil + +*********************************************************************/ +extern "C" HRESULT DAPI RegInitialize( + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); + RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); + + // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW + vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); + + if (NULL == vpfnRegDeleteKeyExW) + { + vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; + } + + vfRegInitialized = TRUE; + +LExit: + return hr; +} + + +/******************************************************************** + RegUninitialize - uninitializes regutil + +*********************************************************************/ +extern "C" void DAPI RegUninitialize( + ) +{ + if (vhAdvApi32Dll) + { + ::FreeLibrary(vhAdvApi32Dll); + vhAdvApi32Dll = NULL; + vpfnRegDeleteKeyExWFromLibrary = NULL; + vpfnRegDeleteKeyExW = NULL; + } + + vfRegInitialized = FALSE; +} + + +/******************************************************************** + RegFunctionOverride - overrides the registry functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI RegFunctionOverride( + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW + ) +{ + vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; + vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW; + vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary; + vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW; + vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW; + vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW; + vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; + vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; + vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; +} + + +/******************************************************************** + RegCreate - creates a registry key. + +*********************************************************************/ +extern "C" HRESULT DAPI RegCreate( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); + RegExitOnWin32Error(er, hr, "Failed to create registry key."); + +LExit: + return hr; +} + + +/******************************************************************** + RegCreate - creates a registry key with extra options. + +*********************************************************************/ +HRESULT DAPI RegCreateEx( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __in BOOL fVolatile, + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, + __out HKEY* phk, + __out_opt BOOL* pfCreated + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwDisposition; + + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition); + RegExitOnWin32Error(er, hr, "Failed to create registry key."); + + if (pfCreated) + { + *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegOpen - opens a registry key. + +*********************************************************************/ +extern "C" HRESULT DAPI RegOpen( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to open registry key."); + +LExit: + return hr; +} + + +/******************************************************************** + RegDelete - deletes a registry key (and optionally it's whole tree). + +*********************************************************************/ +extern "C" HRESULT DAPI RegDelete( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fDeleteTree + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR pszEnumeratedSubKey = NULL; + LPWSTR pszRecursiveSubKey = NULL; + HKEY hkKey = NULL; + REGSAM samDesired = 0; + + if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) + { + hr = E_INVALIDARG; + RegExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); + } + + switch (kbKeyBitness) + { + case REG_KEY_32BIT: + samDesired = KEY_WOW64_32KEY; + break; + case REG_KEY_64BIT: + samDesired = KEY_WOW64_64KEY; + break; + case REG_KEY_DEFAULT: + // Nothing to do + break; + } + + if (fDeleteTree) + { + hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + RegExitOnFailure(hr, "Failed to open this key for enumerating subkeys: %ls", wzSubKey); + + // Yes, keep enumerating the 0th item, because we're deleting it every time + while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) + { + RegExitOnFailure(hr, "Failed to enumerate key 0"); + + hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); + RegExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); + + hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); + RegExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); + } + + hr = S_OK; + } + + if (NULL != vpfnRegDeleteKeyExW) + { + er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); + } + else + { + er = vpfnRegDeleteKeyW(hkRoot, wzSubKey); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to delete registry key."); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(pszEnumeratedSubKey); + ReleaseStr(pszRecursiveSubKey); + + return hr; +} + + +/******************************************************************** + RegKeyEnum - enumerates child registry keys. + +*********************************************************************/ +extern "C" HRESULT DAPI RegKeyEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczKey + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + SIZE_T cb = 0; + DWORD cch = 0; + + if (psczKey && *psczKey) + { + hr = StrMaxLength(*psczKey, &cb); + RegExitOnFailure(hr, "Failed to determine length of string."); + + cch = (DWORD)min(DWORD_MAX, cb); + } + + if (2 > cch) + { + cch = 2; + + hr = StrAlloc(psczKey, cch); + RegExitOnFailure(hr, "Failed to allocate string to minimum size."); + } + + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); + if (ERROR_MORE_DATA == er) + { + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL); + RegExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); + + ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator. + hr = StrAlloc(psczKey, cch); + RegExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); + + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); + } + else if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = E_NOMOREITEMS); + } + RegExitOnWin32Error(er, hr, "Failed to enum registry key."); + + // Always ensure the registry key name is null terminated. +#pragma prefast(push) +#pragma prefast(disable:26018) + (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works. +#pragma prefast(pop) + +LExit: + return hr; +} + + +/******************************************************************** + RegValueEnum - enumerates registry values. + +*********************************************************************/ +HRESULT DAPI RegValueEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczName, + __out_opt DWORD *pdwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD cbValueName = 0; + + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL); + RegExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); + + // Add one for null terminator + ++cbValueName; + + hr = StrAlloc(psczName, cbValueName); + RegExitOnFailure(hr, "Failed to allocate array for registry value name"); + + er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = E_NOMOREITEMS); + } + RegExitOnWin32Error(er, hr, "Failed to enumerate registry value"); + +LExit: + return hr; +} + +/******************************************************************** + RegGetType - reads a registry key value type. + *********************************************************************/ +HRESULT DAPI RegGetType( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD *pdwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry value."); +LExit: + + return hr; +} + +/******************************************************************** + RegReadBinary - reads a registry key binary value. + NOTE: caller is responsible for freeing *ppbBuffer +*********************************************************************/ +HRESULT DAPI RegReadBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T *pcbBuffer + ) +{ + HRESULT hr = S_OK; + LPBYTE pbBuffer = NULL; + DWORD er = ERROR_SUCCESS; + DWORD cb = 0; + DWORD dwType = 0; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); + RegExitOnWin32Error(er, hr, "Failed to get size of registry value."); + + // Zero-length binary values can exist + if (0 < cb) + { + pbBuffer = static_cast(MemAlloc(cb, FALSE)); + RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry value."); + } + + if (REG_BINARY == dwType) + { + *ppbBuffer = pbBuffer; + pbBuffer = NULL; + *pcbBuffer = cb; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseMem(pbBuffer); + + return hr; +} + + +/******************************************************************** + RegReadString - reads a registry key value as a string. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + SIZE_T cbValue = 0; + DWORD cch = 0; + DWORD cb = 0; + DWORD dwType = 0; + LPWSTR sczExpand = NULL; + + if (psczValue && *psczValue) + { + hr = StrMaxLength(*psczValue, &cbValue); + RegExitOnFailure(hr, "Failed to determine length of string."); + + cch = (DWORD)min(DWORD_MAX, cbValue); + } + + if (2 > cch) + { + cch = 2; + + hr = StrAlloc(psczValue, cch); + RegExitOnFailure(hr, "Failed to allocate string to minimum size."); + } + + cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); + if (ERROR_MORE_DATA == er) + { + cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator + hr = StrAlloc(psczValue, cch); + RegExitOnFailure(hr, "Failed to allocate string bigger for registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); + } + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry key."); + + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) + { + // Always ensure the registry value is null terminated. + (*psczValue)[cch - 1] = L'\0'; + + if (REG_EXPAND_SZ == dwType) + { + hr = StrAllocString(&sczExpand, *psczValue, 0); + RegExitOnFailure(hr, "Failed to copy registry value to expand."); + + hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); + RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseStr(sczExpand); + + return hr; +} + + +/******************************************************************** + RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. + +*********************************************************************/ +HRESULT DAPI RegReadStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, + __out DWORD *pcStrings + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwNullCharacters = 0; + DWORD dwType = 0; + DWORD cb = 0; + DWORD cch = 0; + LPCWSTR wzSource = NULL; + LPWSTR sczValue = NULL; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); + if (0 < cb) + { + cch = cb / sizeof(WCHAR); + hr = StrAlloc(&sczValue, cch); + RegExitOnFailure(hr, "Failed to allocate string for registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); + } + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry key."); + + if (cb / sizeof(WCHAR) != cch) + { + hr = E_UNEXPECTED; + RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); + } + + if (REG_MULTI_SZ != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); + } + + // Value exists, but is empty, so no strings to return. + if (2 > cch) + { + *prgsczStrings = NULL; + *pcStrings = 0; + ExitFunction1(hr = S_OK); + } + + // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. + if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) + { + hr = E_INVALIDARG; + RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); + } + + cch = cb / sizeof(WCHAR); + for (DWORD i = 0; i < cch; ++i) + { + if (L'\0' == sczValue[i]) + { + ++dwNullCharacters; + } + } + + // There's one string for every null character encountered (except the extra 1 at the end of the string) + *pcStrings = dwNullCharacters - 1; + hr = MemEnsureArraySize(reinterpret_cast(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); + RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); + +#pragma prefast(push) +#pragma prefast(disable:26010) + wzSource = sczValue; + for (DWORD i = 0; i < *pcStrings; ++i) + { + hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); + RegExitOnFailure(hr, "Failed to allocate copy of string"); + + // Skip past this string + wzSource += lstrlenW(wzSource) + 1; + } +#pragma prefast(pop) + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + +/******************************************************************** + RegReadVersion - reads a registry key value as a version. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pdw64Version + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = 0; + LPWSTR sczVersion = NULL; + + cb = sizeof(DWORD64); + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*pdw64Version), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) + { + hr = RegReadString(hk, wzName, &sczVersion); + RegExitOnFailure(hr, "Failed to read registry version as string."); + + hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); + RegExitOnFailure(hr, "Failed to convert registry string to version."); + } + else if (REG_QWORD == dwType) + { + RegExitOnWin32Error(er, hr, "Failed to read registry key."); + } + else // unexpected data type + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseStr(sczVersion); + + return hr; +} + + +/******************************************************************** + RegReadNumber - reads a DWORD registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = sizeof(DWORD); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pdwValue), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to query registry key value."); + + if (REG_DWORD != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegReadQword - reads a QWORD registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pqwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = sizeof(DWORD64); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pqwValue), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to query registry key value."); + + if (REG_QWORD != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegWriteBinary - writes a registry key value as a binary. + +*********************************************************************/ +HRESULT DAPI RegWriteBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_bcount(cbBuffer) const BYTE *pbBuffer, + __in DWORD cbBuffer + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer); + RegExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); + +LExit: + return hr; +} + + +/******************************************************************** +RegWriteExpandString - writes a registry key value as an expand string. + +Note: if wzValue is NULL the value will be removed. +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteExpandString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue +) +{ + return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ); +} + + +/******************************************************************** + RegWriteString - writes a registry key value as a string. + + Note: if wzValue is NULL the value will be removed. +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue + ) +{ + return WriteStringToRegistry(hk, wzName, wzValue, REG_SZ); +} + + +/******************************************************************** + RegWriteStringFormatted - writes a registry key value as a formatted string. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteStringFormatted( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in __format_string LPCWSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + va_list args; + + va_start(args, szFormat); + hr = StrAllocFormattedArgs(&sczValue, szFormat, args); + va_end(args); + RegExitOnFailure(hr, "Failed to allocate %ls value.", wzName); + + hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ); + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + +/******************************************************************** + RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value + +*********************************************************************/ +HRESULT DAPI RegWriteStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_ecount(cValues) LPWSTR *rgwzValues, + __in DWORD cValues + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR wzCopyDestination = NULL; + LPCWSTR wzWriteValue = NULL; + LPWSTR sczWriteValue = NULL; + DWORD dwTotalStringSize = 0; + DWORD cbTotalStringSize = 0; + DWORD dwTemp = 0; + + if (0 == cValues) + { + wzWriteValue = L"\0"; + } + else + { + // Add space for the null terminator + dwTotalStringSize = 1; + + for (DWORD i = 0; i < cValues; ++i) + { + dwTemp = dwTotalStringSize; + hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize); + RegExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); + } + + hr = StrAlloc(&sczWriteValue, dwTotalStringSize); + RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); + + wzCopyDestination = sczWriteValue; + dwTemp = dwTotalStringSize; + for (DWORD i = 0; i < cValues; ++i) + { + hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); + RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); + + dwTemp -= lstrlenW(rgwzValues[i]) + 1; + wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; + } + + wzWriteValue = sczWriteValue; + } + + hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); + RegExitOnFailure(hr, "Failed to get total string size in bytes"); + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast(wzWriteValue), cbTotalStringSize); + RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); + +LExit: + ReleaseStr(sczWriteValue); + + return hr; +} + +/******************************************************************** + RegWriteNumber - writes a registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof(dwValue)); + RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + +LExit: + return hr; +} + +/******************************************************************** + RegWriteQword - writes a registry key value as a Qword. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast(&qwValue), sizeof(qwValue)); + RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + +LExit: + return hr; +} + +/******************************************************************** + RegQueryKey - queries the key for the number of subkeys and values. + +*********************************************************************/ +extern "C" HRESULT DAPI RegQueryKey( + __in HKEY hk, + __out_opt DWORD* pcSubKeys, + __out_opt DWORD* pcValues + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL); + RegExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); + +LExit: + return hr; +} + +/******************************************************************** +RegKeyReadNumber - reads a DWORD registry key value as a number from +a specified subkey. + +*********************************************************************/ +extern "C" HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegReadNumber(hkKey, wzName, pdwValue); + RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return hr; +} + +/******************************************************************** +RegValueExists - determines whether a named value exists in a +specified subkey. + +*********************************************************************/ +extern "C" BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + DWORD dwType = 0; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegGetType(hkKey, wzName, &dwType); + RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return SUCCEEDED(hr); +} + +static HRESULT WriteStringToRegistry( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue, + __in DWORD dwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + size_t cbValue = 0; + + if (wzValue) + { + hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue); + RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); + + er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), static_cast(cbValue)); + RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); + } + else + { + er = vpfnRegDeleteValueW(hk, wzName); + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + RegExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); + } + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ResrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) +#define ResrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) +#define ResrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) +#define ResrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) +#define ResrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESRUTIL, e, x, s, __VA_ARGS__) +#define ResrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESRUTIL, g, x, s, __VA_ARGS__) + +#define RES_STRINGS_PER_BLOCK 16 + + +BOOL CALLBACK EnumLangIdProc( + __in_opt HMODULE hModule, + __in_z LPCSTR lpType, + __in_z LPCSTR lpName, + __in WORD wLanguage, + __in LONG_PTR lParam + ); + +/******************************************************************** +ResGetStringLangId - get the language id for a string in the string table. + +********************************************************************/ +extern "C" HRESULT DAPI ResGetStringLangId( + __in_opt LPCWSTR wzPath, + __in UINT uID, + __out WORD *pwLangId + ) +{ + Assert(pwLangId); + + HRESULT hr = S_OK; + HINSTANCE hModule = NULL; + DWORD dwBlockId = (uID / RES_STRINGS_PER_BLOCK) + 1; + WORD wFoundLangId = 0; + + if (wzPath && *wzPath) + { + hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); + ResrExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath); + } + +#pragma prefast(push) +#pragma prefast(disable:25068) + if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast(EnumLangIdProc), reinterpret_cast(&wFoundLangId))) +#pragma prefast(pop) + { + ResrExitWithLastError(hr, "Failed to find string language identifier."); + } + + *pwLangId = wFoundLangId; + +LExit: + if (hModule) + { + ::FreeLibrary(hModule); + } + + return hr; +} + + +/******************************************************************** +ResReadString + +NOTE: ppwzString should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI ResReadString( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPWSTR* ppwzString + ) +{ + Assert(hinst && ppwzString); + + HRESULT hr = S_OK; + DWORD cch = 64; // first guess + DWORD cchReturned = 0; + + do + { + hr = StrAlloc(ppwzString, cch); + ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + + cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch); + if (0 == cchReturned) + { + ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); + } + + // if the returned string count is one character too small, it's likely we have + // more data to read + if (cchReturned + 1 == cch) + { + cch *= 2; + hr = S_FALSE; + } + } while (S_FALSE == hr); + ResrExitOnFailure(hr, "Failed to load string resource id: %d", uID); + +LExit: + return hr; +} + + +/******************************************************************** + ResReadStringAnsi + + NOTE: ppszString should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI ResReadStringAnsi( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPSTR* ppszString + ) +{ + Assert(hinst && ppszString); + + HRESULT hr = S_OK; + DWORD cch = 64; // first guess + DWORD cchReturned = 0; + + do + { + hr = StrAnsiAlloc(ppszString, cch); + ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + +#pragma prefast(push) +#pragma prefast(disable:25068) + cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch); +#pragma prefast(pop) + if (0 == cchReturned) + { + ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); + } + + // if the returned string count is one character too small, it's likely we have + // more data to read + if (cchReturned + 1 == cch) + { + cch *= 2; + hr = S_FALSE; + } + } while (S_FALSE == hr); + ResrExitOnFailure(hr, "failed to load string resource id: %d", uID); + +LExit: + return hr; +} + + +/******************************************************************** +ResReadData - returns a pointer to the specified resource data + +NOTE: there is no "free" function for this call +********************************************************************/ +extern "C" HRESULT DAPI ResReadData( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szDataName, + __deref_out_bcount(*pcb) PVOID *ppv, + __out DWORD *pcb + ) +{ + Assert(szDataName); + Assert(ppv); + + HRESULT hr = S_OK; + HRSRC hRsrc = NULL; + HGLOBAL hData = NULL; + DWORD cbData = 0; + +#pragma prefast(push) +#pragma prefast(disable:25068) + hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); +#pragma prefast(pop) + ResrExitOnNullWithLastError(hRsrc, hr, "Failed to find resource."); + + hData = ::LoadResource(hinst, hRsrc); + ResrExitOnNullWithLastError(hData, hr, "Failed to load resource."); + + cbData = ::SizeofResource(hinst, hRsrc); + if (!cbData) + { + ResrExitWithLastError(hr, "Failed to get size of resource."); + } + + *ppv = ::LockResource(hData); + ResrExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource."); + *pcb = cbData; + +LExit: + return hr; +} + + +/******************************************************************** +ResExportDataToFile - extracts the resource data to the specified target file + +********************************************************************/ +extern "C" HRESULT DAPI ResExportDataToFile( + __in_z LPCSTR szDataName, + __in_z LPCWSTR wzTargetFile, + __in DWORD dwCreationDisposition + ) +{ + HRESULT hr = S_OK; + PVOID pData = NULL; + DWORD cbData = 0; + DWORD cbWritten = 0; + HANDLE hFile = INVALID_HANDLE_VALUE; + BOOL bCreatedFile = FALSE; + + hr = ResReadData(NULL, szDataName, &pData, &cbData); + ResrExitOnFailure(hr, "Failed to GetData from %s.", szDataName); + + hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ResrExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile); + } + bCreatedFile = TRUE; + + if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL)) + { + ResrExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile); + } + +LExit: + ReleaseFile(hFile); + + if (FAILED(hr)) + { + if (bCreatedFile) + { + ::DeleteFileW(wzTargetFile); + } + } + + return hr; +} + + +BOOL CALLBACK EnumLangIdProc( + __in_opt HMODULE /* hModule */, + __in_z LPCSTR /* lpType */, + __in_z LPCSTR /* lpName */, + __in WORD wLanguage, + __in LONG_PTR lParam + ) +{ + WORD *pwLangId = reinterpret_cast(lParam); + + *pwLangId = wLanguage; + return TRUE; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ReswExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) +#define ReswExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) +#define ReswExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) +#define ReswExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) +#define ReswExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESWUTIL, e, x, s, __VA_ARGS__) +#define ReswExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESWUTIL, g, x, s, __VA_ARGS__) + +#define RES_STRINGS_PER_BLOCK 16 + +// Internal data structure format for a string block in a resource table. +// Note: Strings are always stored as UNICODED. +typedef struct _RES_STRING_BLOCK +{ + DWORD dwBlockId; + WORD wLangId; + LPWSTR rgwz[RES_STRINGS_PER_BLOCK]; +} RES_STRING_BLOCK; + + +// private functions +static HRESULT StringBlockInitialize( + __in_opt HINSTANCE hModule, + __in DWORD dwBlockId, + __in WORD wLangId, + __in RES_STRING_BLOCK* pStrBlock + ); +static void StringBlockUnitialize( + __in RES_STRING_BLOCK* pStrBlock + ); +static HRESULT StringBlockChangeString( + __in RES_STRING_BLOCK* pStrBlock, + __in DWORD dwStringId, + __in_z LPCWSTR szData + ); +static HRESULT StringBlockConvertToResourceData( + __in const RES_STRING_BLOCK* pStrBlock, + __deref_out_bcount(*pcbData) LPVOID* ppvData, + __out DWORD* pcbData + ); +static HRESULT StringBlockConvertFromResourceData( + __in RES_STRING_BLOCK* pStrBlock, + __in_bcount(cbData) LPCVOID pvData, + __in SIZE_T cbData + ); + + +/******************************************************************** +ResWriteString - sets the string into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResWriteString( + __in_z LPCWSTR wzResourceFile, + __in DWORD dwDataId, + __in_z LPCWSTR wzData, + __in WORD wLangId + ) +{ + Assert(wzResourceFile); + Assert(wzData); + + HRESULT hr = S_OK; + HINSTANCE hModule = NULL; + HANDLE hUpdate = NULL; + RES_STRING_BLOCK StrBlock = { }; + LPVOID pvData = NULL; + DWORD cbData = 0; + + DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1; + DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK); + + hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); + ReswExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); + + hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock); + ReswExitOnFailure(hr, "Failed to get string block to update."); + + hr = StringBlockChangeString(&StrBlock, dwStringId, wzData); + ReswExitOnFailure(hr, "Failed to update string block string."); + + hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData); + ReswExitOnFailure(hr, "Failed to convert string block to resource data."); + + ::FreeLibrary(hModule); + hModule = NULL; + + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); + ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + + if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData)) + { + ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); + } + + if (!::EndUpdateResource(hUpdate, FALSE)) + { + ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + } + + hUpdate = NULL; + +LExit: + ReleaseMem(pvData); + + StringBlockUnitialize(&StrBlock); + + if (hUpdate) + { + ::EndUpdateResource(hUpdate, TRUE); + } + + if (hModule) + { + ::FreeLibrary(hModule); + } + + return hr; +} + + +/******************************************************************** +ResWriteData - sets the data into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResWriteData( + __in_z LPCWSTR wzResourceFile, + __in_z LPCSTR szDataName, + __in PVOID pData, + __in DWORD cbData + ) +{ + Assert(wzResourceFile); + Assert(szDataName); + Assert(pData); + Assert(cbData); + + HRESULT hr = S_OK; + HANDLE hUpdate = NULL; + + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); + ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + + if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData)) + { + ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); + } + + if (!::EndUpdateResource(hUpdate, FALSE)) + { + ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + } + + hUpdate = NULL; + +LExit: + if (hUpdate) + { + ::EndUpdateResource(hUpdate, TRUE); + } + + return hr; +} + + +/******************************************************************** +ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResImportDataFromFile( + __in_z LPCWSTR wzTargetFile, + __in_z LPCWSTR wzSourceFile, + __in_z LPCSTR szDataName + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD cbFile = 0; + HANDLE hMap = NULL; + PVOID pv = NULL; + + hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ReswExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); + } + + cbFile = ::GetFileSize(hFile, NULL); + if (!cbFile) + { + ReswExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); + } + + hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + ReswExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); + + pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile); + ReswExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); + + hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile); + ReswExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); + +LExit: + if (pv) + { + ::UnmapViewOfFile(pv); + } + + if (hMap) + { + ::CloseHandle(hMap); + } + + ReleaseFile(hFile); + + return hr; +} + + +static HRESULT StringBlockInitialize( + __in_opt HINSTANCE hModule, + __in DWORD dwBlockId, + __in WORD wLangId, + __in RES_STRING_BLOCK* pStrBlock + ) +{ + HRESULT hr = S_OK; + HRSRC hRsrc = NULL; + HGLOBAL hData = NULL; + LPCVOID pvData = NULL; // does not need to be freed + DWORD cbData = 0; + + hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId); + ReswExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); + + hData = ::LoadResource(hModule, hRsrc); + ReswExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); + + cbData = ::SizeofResource(hModule, hRsrc); + if (!cbData) + { + ReswExitWithLastError(hr, "Failed to ::SizeofResource."); + } + + pvData = ::LockResource(hData); + ReswExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); + + pStrBlock->dwBlockId = dwBlockId; + pStrBlock->wLangId = wLangId; + + hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData); + ReswExitOnFailure(hr, "Failed to convert string block from resource data."); + +LExit: + return hr; +} + + +static void StringBlockUnitialize( + __in RES_STRING_BLOCK* pStrBlock + ) +{ + if (pStrBlock) + { + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + ReleaseNullMem(pStrBlock->rgwz[i]); + } + } +} + + +static HRESULT StringBlockChangeString( + __in RES_STRING_BLOCK* pStrBlock, + __in DWORD dwStringId, + __in_z LPCWSTR szData + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzData = NULL; + size_t cchData = 0; + + hr = ::StringCchLengthW(szData, STRSAFE_MAX_LENGTH, &cchData); + ReswExitOnRootFailure(hr, "Failed to get block string length."); + + pwzData = static_cast(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); + ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); + + hr = ::StringCchCopyW(pwzData, cchData + 1, szData); + ReswExitOnRootFailure(hr, "Failed to copy new block string."); + + ReleaseNullMem(pStrBlock->rgwz[dwStringId]); + + pStrBlock->rgwz[dwStringId] = pwzData; + pwzData = NULL; + +LExit: + ReleaseMem(pwzData); + + return hr; +} + + +static HRESULT StringBlockConvertToResourceData( + __in const RES_STRING_BLOCK* pStrBlock, + __deref_out_bcount(*pcbData) LPVOID* ppvData, + __out DWORD* pcbData + ) +{ + HRESULT hr = S_OK; + DWORD cbData = 0; + LPVOID pvData = NULL; + WCHAR* pwz = NULL; + + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1); + } + cbData *= sizeof(WCHAR); + + pvData = MemAlloc(cbData, TRUE); + ReswExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); + + pwz = static_cast(pvData); + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + DWORD cch = lstrlenW(pStrBlock->rgwz[i]); + + *pwz = static_cast(cch); + ++pwz; + + for (DWORD j = 0; j < cch; ++j) + { + *pwz = pStrBlock->rgwz[i][j]; + ++pwz; + } + } + + *pcbData = cbData; + *ppvData = pvData; + pvData = NULL; + +LExit: + ReleaseMem(pvData); + + return hr; +} + + +static HRESULT StringBlockConvertFromResourceData( + __in RES_STRING_BLOCK* pStrBlock, + __in_bcount(cbData) LPCVOID pvData, + __in SIZE_T cbData + ) +{ + UNREFERENCED_PARAMETER(cbData); + HRESULT hr = S_OK; + LPCWSTR pwzParse = static_cast(pvData); + + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + DWORD cchParse = static_cast(*pwzParse); + ++pwzParse; + + pStrBlock->rgwz[i] = static_cast(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE)); + ReswExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); + + hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ReswExitOnFailure(hr, "Failed to copy parsed resource data into string block."); + + pwzParse += cchParse; + } + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include "rexutil.h" + + +// Exit macros +#define RexExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) +#define RexExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) +#define RexExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) +#define RexExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) +#define RexExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REXUTIL, e, x, s, __VA_ARGS__) +#define RexExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REXUTIL, g, x, s, __VA_ARGS__) + +// +// static globals +// +static HMODULE vhCabinetDll = NULL; +static HFDI vhfdi = NULL; +static ERF verf; + +static FAKE_FILE vrgffFileTable[FILETABLESIZE]; +static DWORD vcbRes; +static LPCBYTE vpbRes; +static CHAR vszResource[MAX_PATH]; +static REX_CALLBACK_WRITE vpfnWrite = NULL; + +static HRESULT vhrLastError = S_OK; + +// +// structs +// +struct REX_CALLBACK_STRUCT +{ + BOOL fStopExtracting; // flag set when no more files are needed + LPCWSTR pwzExtract; // file to extract ("*" means extract all) + LPCWSTR pwzExtractDir; // directory to extract files to + LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*") + + // possible user data + REX_CALLBACK_PROGRESS pfnProgress; + LPVOID pvContext; +}; + +// +// prototypes +// +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize); +static __callback void DIAMONDAPI RexFree(LPVOID pvData); +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode); +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb); +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb); +static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf); +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype); +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify); + + +/******************************************************************** + RexInitialize - initializes internal static variables + +*******************************************************************/ +extern "C" HRESULT RexInitialize() +{ + Assert(!vhfdi); + + HRESULT hr = S_OK; + + vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf); + if (!vhfdi) + { + hr = E_FAIL; + RexExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here + } + + ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable)); + +LExit: + if (FAILED(hr)) + { + ::FDIDestroy(vhfdi); + vhfdi = NULL; + } + + return hr; +} + + +/******************************************************************** + RexUninitialize - initializes internal static variables + +*******************************************************************/ +extern "C" void RexUninitialize() +{ + if (vhfdi) + { + ::FDIDestroy(vhfdi); + vhfdi = NULL; + } +} + + +/******************************************************************** + RexExtract - extracts one or all files from a resource cabinet + + NOTE: wzExtractId can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + wzExtractName is ignored if wzExtractId is "*" +*******************************************************************/ +extern "C" HRESULT RexExtract( + __in_z LPCSTR szResource, + __in_z LPCWSTR wzExtractId, + __in_z LPCWSTR wzExtractDir, + __in_z LPCWSTR wzExtractName, + __in REX_CALLBACK_PROGRESS pfnProgress, + __in REX_CALLBACK_WRITE pfnWrite, + __in LPVOID pvContext + ) +{ + Assert(vhfdi); + HRESULT hr = S_OK; + BOOL fResult; + + HRSRC hResInfo = NULL; + HANDLE hRes = NULL; + + REX_CALLBACK_STRUCT rcs; + + // remember the write callback + vpfnWrite = pfnWrite; + + // + // load the cabinet resource + // + hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + RexExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); + //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10)); + //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info"); + + hRes = ::LoadResource(NULL, hResInfo); + RexExitOnNullWithLastError(hRes, hr, "failed to load resource"); + + vcbRes = ::SizeofResource(NULL, hResInfo); + vpbRes = (const BYTE*)::LockResource(hRes); + + // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it + + // + // convert the resource name to multi-byte + // + //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL)) + //{ + // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); + //} + + hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); + RexExitOnFailure(hr, "Failed to copy resource name to global."); + + // + // iterate through files in cabinet extracting them to the callback function + // + rcs.fStopExtracting = FALSE; + rcs.pwzExtract = wzExtractId; + rcs.pwzExtractDir = wzExtractDir; + rcs.pwzExtractName = wzExtractName; + rcs.pfnProgress = pfnProgress; + rcs.pvContext = pvContext; + + fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast(&rcs)); + if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure + { + hr = vhrLastError; // TODO: put verf info in trace message here + } + +LExit: + return hr; +} + + +/**************************************************************************** + default extract routines + +****************************************************************************/ +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize) +{ + return MemAlloc(dwSize, FALSE); +} + + +static __callback void DIAMONDAPI RexFree(LPVOID pvData) +{ + MemFree(pvData); +} + + +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + int i = 0; + + // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) + { + hr = E_OUTOFMEMORY; + RexExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); + } + + // find an empty spot in the fake file table + for (i = 0; i < FILETABLESIZE; ++i) + { + if (!vrgffFileTable[i].fUsed) + { + break; + } + } + + // we should never run out of space in the fake file table + if (FILETABLESIZE <= i) + { + hr = E_OUTOFMEMORY; + RexExitOnFailure(hr, "File table exceeded"); + } + + if (0 == lstrcmpA(vszResource, pszFile)) + { + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = MEMORY_FILE; + vrgffFileTable[i].mfFile.vpStart = static_cast(vpbRes); + vrgffFileTable[i].mfFile.uiCurrent = 0; + vrgffFileTable[i].mfFile.uiLength = vcbRes; + } + else // it's a real file + { + hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + RexExitWithLastError(hr, "failed to open file: %s", pszFile); + } + + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = NORMAL_FILE; + vrgffFileTable[i].hFile = hFile; + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : i; +} + + +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + DWORD cbRead = 0; + DWORD cbAvailable = 0; + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + // ensure that we don't read past the length of the resource + cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent; + cbRead = cb < cbAvailable? cb : cbAvailable; + + memcpy(pv, static_cast(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead); + + vrgffFileTable[hf].mfFile.uiCurrent += cbRead; + } + else // NORMAL_FILE + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL)) + { + RexExitWithLastError(hr, "failed to read during cabinet extraction"); + } + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb) +{ + Assert(vrgffFileTable[hf].fUsed); + Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file + + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + if (!::WriteFile(reinterpret_cast(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL)) + { + RexExitWithLastError(hr, "failed to write during cabinet extraction"); + } + + // call the writer callback if defined + if (vpfnWrite) + { + vpfnWrite(cb); + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + RexExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + } + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + if (FILE_BEGIN == dwMoveMethod) + { + vrgffFileTable[hf].mfFile.uiCurrent = dist; + } + else if (FILE_CURRENT == dwMoveMethod) + { + vrgffFileTable[hf].mfFile.uiCurrent += dist; + } + else // FILE_END + { + vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist; + } + + lMove = vrgffFileTable[hf].mfFile.uiCurrent; + } + else // NORMAL_FILE + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod); + if (0xFFFFFFFF == lMove) + { + RexExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + } + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : lMove; +} + + +__callback int FAR DIAMONDAPI RexClose(INT_PTR hf) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + vrgffFileTable[hf].mfFile.vpStart = NULL; + vrgffFileTable[hf].mfFile.uiCurrent = 0; + vrgffFileTable[hf].mfFile.uiLength = 0; + } + else + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + if (!::CloseHandle(vrgffFileTable[hf].hFile)) + { + RexExitWithLastError(hr, "failed to close file during cabinet extraction"); + } + + vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE; + } + + vrgffFileTable[hf].fUsed = FALSE; + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : 0; +} + + +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify) +{ + Assert(pFDINotify->pv); + + HRESULT hr = S_OK; + int ipResult = 0; // result to return on success + HANDLE hFile = INVALID_HANDLE_VALUE; + + REX_CALLBACK_STRUCT* prcs = static_cast(pFDINotify->pv); + LPCSTR sz; + WCHAR wz[MAX_PATH]; + FILETIME ft; + int i = 0; + + switch (iNotification) + { + case fdintCOPY_FILE: // beGIN extracting a resource from cabinet + Assert(pFDINotify->psz1); + + if (prcs->fStopExtracting) + { + ExitFunction1(hr = S_FALSE); // no more extracting + } + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (prcs->pfnProgress) + { + hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); + if (S_OK != hr) + { + ExitFunction(); + } + } + + if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) + { + // get the created date for the resource in the cabinet + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) + { + RexExitWithLastError(hr, "failed to get time for resource: %ls", wz); + } + + WCHAR wzPath[MAX_PATH]; + + hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); + RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); + + if (L'*' == *prcs->pwzExtract) + { + hr = ::StringCchCatW(wzPath, countof(wzPath), wz); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + } + else + { + Assert(*prcs->pwzExtractName); + + hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); + } + + // Quickly chop off the file name part of the path to ensure the path exists + // then put the file name back on the path (by putting the first character + // back over the null terminator). + LPWSTR wzFile = PathFile(wzPath); + WCHAR wzFileFirstChar = *wzFile; + *wzFile = L'\0'; + + hr = DirEnsureExists(wzPath, NULL); + RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); + + hr = S_OK; + + *wzFile = wzFileFirstChar; + + // find an empty spot in the fake file table + for (i = 0; i < FILETABLESIZE; ++i) + { + if (!vrgffFileTable[i].fUsed) + { + break; + } + } + + // we should never run out of space in the fake file table + if (FILETABLESIZE <= i) + { + hr = E_OUTOFMEMORY; + RexExitOnFailure(hr, "File table exceeded"); + } + + // open the file + hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + RexExitWithLastError(hr, "failed to open file: %ls", wzPath); + } + + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = NORMAL_FILE; + vrgffFileTable[i].hFile = hFile; + + ipResult = i; + + ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) + + if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) + { + if (::SetEndOfFile(vrgffFileTable[i].hFile)) + { + ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer + } + } + } + else // resource wasn't requested, skip it + { + hr = S_OK; + ipResult = 0; + } + + break; + case fdintCLOSE_FILE_INFO: // resource extraction complete + Assert(pFDINotify->hf && pFDINotify->psz1); + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + RexClose(pFDINotify->hf); + + if (prcs->pfnProgress) + { + hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); + } + + if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going + { + ipResult = TRUE; + } + else // something went wrong or we only needed to extract one file + { + hr = S_OK; + ipResult = FALSE; + prcs->fStopExtracting = TRUE; + } + + break; + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + default: + AssertSz(FALSE, "RexCallback() - unknown FDI notification command"); + }; + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return (S_OK == hr) ? ipResult : -1; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include + + +// Exit macros +#define RmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) +#define RmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) +#define RmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) +#define RmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) +#define RmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RMUTIL, e, x, s, __VA_ARGS__) +#define RmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RMUTIL, g, x, s, __VA_ARGS__) + +#define ARRAY_GROWTH_SIZE 5 + +typedef DWORD (WINAPI *PFNRMJOINSESSION)( + __out DWORD *pSessionHandle, + __in_z const WCHAR strSessionKey[] + ); + +typedef DWORD (WINAPI *PFNRMENDSESSION)( + __in DWORD dwSessionHandle + ); + +typedef DWORD (WINAPI *PFNRMREGISTERRESOURCES)( + __in DWORD dwSessionHandle, + __in UINT nFiles, + __in_z_opt LPWSTR *rgsFilenames, + __in UINT nApplications, + __in_opt RM_UNIQUE_PROCESS *rgApplications, + __in UINT nServices, + __in_z_opt LPWSTR *rgsServiceNames + ); + +typedef struct _RMU_SESSION +{ + CRITICAL_SECTION cs; + DWORD dwSessionHandle; + BOOL fStartedSessionHandle; + BOOL fInitialized; + + UINT cFilenames; + LPWSTR *rgsczFilenames; + + UINT cApplications; + RM_UNIQUE_PROCESS *rgApplications; + + UINT cServiceNames; + LPWSTR *rgsczServiceNames; + +} RMU_SESSION; + +static volatile LONG vcRmuInitialized = 0; +static HMODULE vhModule = NULL; +static PFNRMJOINSESSION vpfnRmJoinSession = NULL; +static PFNRMENDSESSION vpfnRmEndSession = NULL; +static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL; + +static HRESULT RmuInitialize(); +static void RmuUninitialize(); + +static HRESULT RmuApplicationArrayAlloc( + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, + __inout LPUINT pcApplications, + __in DWORD dwProcessId, + __in FILETIME ProcessStartTime + ); + +static HRESULT RmuApplicationArrayFree( + __in RM_UNIQUE_PROCESS *rgApplications + ); + +#define ReleaseNullApplicationArray(rg, c) { if (rg) { RmuApplicationArrayFree(rg); c = 0; rg = NULL; } } + +/******************************************************************** +RmuJoinSession - Joins an existing Restart Manager session. + +********************************************************************/ +extern "C" HRESULT DAPI RmuJoinSession( + __out PRMU_SESSION *ppSession, + __in_z LPCWSTR wzSessionKey + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + PRMU_SESSION pSession = NULL; + + *ppSession = NULL; + + pSession = static_cast(MemAlloc(sizeof(RMU_SESSION), TRUE)); + RmExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); + + hr = RmuInitialize(); + RmExitOnFailure(hr, "Failed to initialize Restart Manager."); + + er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey); + RmExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); + + ::InitializeCriticalSection(&pSession->cs); + pSession->fInitialized = TRUE; + + *ppSession = pSession; + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(pSession); + } + + return hr; +} + +/******************************************************************** +RmuAddFile - Adds the file path to the Restart Manager session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddFile( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pSession->cs); + + // Create or grow the jagged array. + hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0); + RmExitOnFailure(hr, "Failed to add the filename to the array."); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuAddProcessById - Adds the process ID to the Restart Manager sesion. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddProcessById( + __in PRMU_SESSION pSession, + __in DWORD dwProcessId + ) +{ + HRESULT hr = S_OK; + HANDLE hProcess = NULL; + FILETIME CreationTime = {}; + FILETIME ExitTime = {}; + FILETIME KernelTime = {}; + FILETIME UserTime = {}; + BOOL fLocked = FALSE; + + HANDLE hToken = NULL; + TOKEN_PRIVILEGES priv = { 0 }; + TOKEN_PRIVILEGES* pPrevPriv = NULL; + DWORD cbPrevPriv = 0; + DWORD er = ERROR_SUCCESS; + BOOL fAdjustedPrivileges = FALSE; + BOOL fElevated = FALSE; + ProcElevated(::GetCurrentProcess(), &fElevated); + + // Must be elevated to adjust process privileges + if (fElevated) { + // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context. + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + RmExitWithLastError(hr, "Failed to get process token."); + } + + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) + { + RmExitWithLastError(hr, "Failed to get debug privilege LUID."); + } + + cbPrevPriv = sizeof(TOKEN_PRIVILEGES); + pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); + RmExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); + + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + { + LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); + RmExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); + pPrevPriv = static_cast(pv); + + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + { + RmExitWithLastError(hr, "Failed to get debug privilege LUID."); + } + } + + fAdjustedPrivileges = TRUE; + } + + hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); + if (hProcess) + { + if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime)) + { + RmExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId); + } + + ::EnterCriticalSection(&pSession->cs); + fLocked = TRUE; + hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime); + RmExitOnFailure(hr, "Failed to add the application to the array."); + } + else + { + er = ::GetLastError(); + if (ERROR_ACCESS_DENIED == er) + { + // OpenProcess will fail when not elevated and the target process is in another user context. Let the caller log and continue. + hr = E_NOTFOUND; + } + else + { + RmExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId); + } + } + +LExit: + if (hProcess) + { + ::CloseHandle(hProcess); + } + + if (fAdjustedPrivileges) + { + ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL); + } + + ReleaseMem(pPrevPriv); + ReleaseHandle(hToken); + + if (fLocked) + { + ::LeaveCriticalSection(&pSession->cs); + } + + return hr; +} + +/******************************************************************** +RmuAddProcessesByName - Adds all processes by the given process name + to the Restart Manager Session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddProcessesByName( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzProcessName + ) +{ + HRESULT hr = S_OK; + DWORD *pdwProcessIds = NULL; + DWORD cProcessIds = 0; + BOOL fNotFound = FALSE; + + hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds); + RmExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName); + + for (DWORD i = 0; i < cProcessIds; ++i) + { + hr = RmuAddProcessById(pSession, pdwProcessIds[i]); + if (E_NOTFOUND == hr) + { + // RmuAddProcessById returns E_NOTFOUND when this setup is not elevated and OpenProcess returned access denied (target process running under another user account). + fNotFound = TRUE; + } + else + { + RmExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]); + } + } + + // 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. + if (fNotFound) + { + hr = E_NOTFOUND; + } + +LExit: + ReleaseMem(pdwProcessIds); + + return hr; +} + +/******************************************************************** +RmuAddService - Adds the service name to the Restart Manager session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddService( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzServiceName + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pSession->cs); + + hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0); + RmExitOnFailure(hr, "Failed to add the service name to the array."); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuRegisterResources - Registers resources for the Restart Manager. + +This should be called rarely because it is expensive to run. Call +functions like RmuAddFile for multiple resources then commit them +as a batch of updates to RmuRegisterResources. + +Duplicate resources appear to be handled by Restart Manager. +Only one WM_QUERYENDSESSION is being sent for each top-level window. + +********************************************************************/ +extern "C" HRESULT DAPI RmuRegisterResources( + __in PRMU_SESSION pSession + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); + + ::EnterCriticalSection(&pSession->cs); + + er = vpfnRmRegisterResources( + pSession->dwSessionHandle, + pSession->cFilenames, + pSession->rgsczFilenames, + pSession->cApplications, + pSession->rgApplications, + pSession->cServiceNames, + pSession->rgsczServiceNames + ); + RmExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); + + // Empty the arrays if registered in case additional resources are added later. + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuEndSession - Ends the session. + +If the session was joined by RmuJoinSession, any remaining resources +are registered before the session is ended (left). + +********************************************************************/ +extern "C" HRESULT DAPI RmuEndSession( + __in PRMU_SESSION pSession + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); + + // Make sure all resources are registered if we joined the session. + if (!pSession->fStartedSessionHandle) + { + hr = RmuRegisterResources(pSession); + RmExitOnFailure(hr, "Failed to register remaining resources."); + } + + er = vpfnRmEndSession(pSession->dwSessionHandle); + RmExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); + +LExit: + if (pSession->fInitialized) + { + ::DeleteCriticalSection(&pSession->cs); + } + + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); + ReleaseNullMem(pSession); + + RmuUninitialize(); + + return hr; +} + +static HRESULT RmuInitialize() +{ + HRESULT hr = S_OK; + HMODULE hModule = NULL; + + LONG iRef = ::InterlockedIncrement(&vcRmuInitialized); + if (1 == iRef && !vhModule) + { + hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule); + RmExitOnFailure(hr, "Failed to load the rstrtmgr.dll module."); + + vpfnRmJoinSession = reinterpret_cast(::GetProcAddress(hModule, "RmJoinSession")); + RmExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll."); + + vpfnRmRegisterResources = reinterpret_cast(::GetProcAddress(hModule, "RmRegisterResources")); + RmExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll."); + + vpfnRmEndSession = reinterpret_cast(::GetProcAddress(hModule, "RmEndSession")); + RmExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll."); + + vhModule = hModule; + } + +LExit: + return hr; +} + +static void RmuUninitialize() +{ + LONG iRef = ::InterlockedDecrement(&vcRmuInitialized); + if (0 == iRef && vhModule) + { + vpfnRmJoinSession = NULL; + vpfnRmEndSession = NULL; + vpfnRmRegisterResources = NULL; + + ::FreeLibrary(vhModule); + vhModule = NULL; + } +} + +static HRESULT RmuApplicationArrayAlloc( + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, + __inout LPUINT pcApplications, + __in DWORD dwProcessId, + __in FILETIME ProcessStartTime + ) +{ + HRESULT hr = S_OK; + RM_UNIQUE_PROCESS *pApplication = NULL; + + hr = MemEnsureArraySize(reinterpret_cast(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE); + RmExitOnFailure(hr, "Failed to allocate memory for the application array."); + + pApplication = static_cast(&(*prgApplications)[*pcApplications]); + pApplication->dwProcessId = dwProcessId; + pApplication->ProcessStartTime = ProcessStartTime; + + ++(*pcApplications); + +LExit: + return hr; +} + +static HRESULT RmuApplicationArrayFree( + __in RM_UNIQUE_PROCESS *rgApplications + ) +{ + HRESULT hr = S_OK; + + hr = MemFree(rgApplications); + RmExitOnFailure(hr, "Failed to free memory for the application array."); + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define RssExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) +#define RssExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) +#define RssExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) +#define RssExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) +#define RssExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RSSUTIL, e, x, s, __VA_ARGS__) +#define RssExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RSSUTIL, g, x, s, __VA_ARGS__) + +static HRESULT ParseRssDocument( + __in IXMLDOMDocument *pixd, + __out RSS_CHANNEL **ppChannel + ); +static HRESULT ParseRssChannel( + __in IXMLDOMNode *pixnChannel, + __out RSS_CHANNEL **ppChannel + ); +static HRESULT ParseRssItem( + __in IXMLDOMNode *pixnItem, + __in DWORD cItem, + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ); +static HRESULT ParseRssUnknownElement( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement + ); +static HRESULT ParseRssUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ); +static void FreeRssUnknownElementList( + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement + ); +static void FreeRssUnknownAttributeList( + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ); + + +/******************************************************************** + RssInitialize - Initialize RSS utilities. + +*********************************************************************/ +extern "C" HRESULT DAPI RssInitialize() +{ + return XmlInitialize(); +} + + +/******************************************************************** + RssUninitialize - Uninitialize RSS utilities. + +*********************************************************************/ +extern "C" void DAPI RssUninitialize() +{ + XmlUninitialize(); +} + + +/******************************************************************** + RssParseFromString - parses out an RSS channel from a string. + +*********************************************************************/ +extern "C" HRESULT DAPI RssParseFromString( + __in_z LPCWSTR wzRssString, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(wzRssString); + Assert(ppChannel); + + HRESULT hr = S_OK; + RSS_CHANNEL *pNewChannel = NULL; + IXMLDOMDocument *pixdRss = NULL; + + hr = XmlLoadDocument(wzRssString, &pixdRss); + RssExitOnFailure(hr, "Failed to load RSS string as XML document."); + + hr = ParseRssDocument(pixdRss, &pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS document."); + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseObject(pixdRss); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + RssParseFromFile - parses out an RSS channel from a file path. + +*********************************************************************/ +extern "C" HRESULT DAPI RssParseFromFile( + __in_z LPCWSTR wzRssFile, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(wzRssFile); + Assert(ppChannel); + + HRESULT hr = S_OK; + RSS_CHANNEL *pNewChannel = NULL; + IXMLDOMDocument *pixdRss = NULL; + + hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss); + RssExitOnFailure(hr, "Failed to load RSS string as XML document."); + + hr = ParseRssDocument(pixdRss, &pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS document."); + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseObject(pixdRss); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + RssFreeChannel - parses out an RSS channel from a string. + +*********************************************************************/ +extern "C" void DAPI RssFreeChannel( + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ) +{ + if (pChannel) + { + for (DWORD i = 0; i < pChannel->cItems; ++i) + { + ReleaseStr(pChannel->rgItems[i].wzTitle); + ReleaseStr(pChannel->rgItems[i].wzLink); + ReleaseStr(pChannel->rgItems[i].wzDescription); + ReleaseStr(pChannel->rgItems[i].wzGuid); + ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl); + ReleaseStr(pChannel->rgItems[i].wzEnclosureType); + + FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements); + } + + ReleaseStr(pChannel->wzTitle); + ReleaseStr(pChannel->wzLink); + ReleaseStr(pChannel->wzDescription); + FreeRssUnknownElementList(pChannel->pUnknownElements); + + MemFree(pChannel); + } +} + + +/******************************************************************** + ParseRssDocument - parses out an RSS channel from a loaded XML DOM document. + +*********************************************************************/ +static HRESULT ParseRssDocument( + __in IXMLDOMDocument *pixd, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(pixd); + Assert(ppChannel); + + HRESULT hr = S_OK; + IXMLDOMElement *pRssElement = NULL; + IXMLDOMNodeList *pChannelNodes = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + RSS_CHANNEL *pNewChannel = NULL; + + // + // Get the document element and start processing channels. + // + hr = pixd ->get_documentElement(&pRssElement); + RssExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); + + hr = pRssElement->get_childNodes(&pChannelNodes); + RssExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); + + while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"channel")) + { + hr = ParseRssChannel(pNode, &pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS channel."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pChannelNodes); + ReleaseObject(pRssElement); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + ParseRssChannel - parses out an RSS channel from a loaded XML DOM element. + +*********************************************************************/ +static HRESULT ParseRssChannel( + __in IXMLDOMNode *pixnChannel, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(pixnChannel); + Assert(ppChannel); + + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + RSS_CHANNEL *pNewChannel = NULL; + long cItems = 0; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + + // + // First, calculate how many RSS items we're going to have and allocate + // the RSS_CHANNEL structure + // + hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList); + RssExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); + + hr = pNodeList->get_length(&cItems); + RssExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); + + pNewChannel = static_cast(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE)); + RssExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); + + pNewChannel->cItems = cItems; + + // + // Process the elements under a channel now. + // + hr = pixnChannel->get_childNodes(&pNodeList); + RssExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); + + cItems = 0; // reset the counter and use this to walk through the channel items + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"title")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel title."); + + hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS channel title."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel link."); + + hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS channel link."); + } + else if (0 == lstrcmpW(bstrNodeName, L"description")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel description."); + + hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS channel description."); + } + else if (0 == lstrcmpW(bstrNodeName, L"ttl")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel description."); + + pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10); + } + else if (0 == lstrcmpW(bstrNodeName, L"item")) + { + hr = ParseRssItem(pNode, cItems, pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS item."); + + ++cItems; + } + else + { + hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements); + RssExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeValue); + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + ParseRssItem - parses out an RSS item from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseRssItem( + __in IXMLDOMNode *pixnItem, + __in DWORD cItem, + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ) +{ + HRESULT hr = S_OK; + + RSS_ITEM *pItem = NULL; + IXMLDOMNodeList *pNodeList = NULL; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + + // + // First make sure we're dealing with a valid item. + // + if (pChannel->cItems <= cItem) + { + hr = E_UNEXPECTED; + RssExitOnFailure(hr, "Unexpected number of items parsed."); + } + + pItem = pChannel->rgItems + cItem; + + // + // Process the elements under an item now. + // + hr = pixnItem->get_childNodes(&pNodeList); + RssExitOnFailure(hr, "Failed to get child nodes of RSS item element."); + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"title")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel title."); + + hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item title."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel link."); + + hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item link."); + } + else if (0 == lstrcmpW(bstrNodeName, L"description")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item description."); + + hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item description."); + } + else if (0 == lstrcmpW(bstrNodeName, L"guid")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item guid."); + + hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item guid."); + } + else if (0 == lstrcmpW(bstrNodeName, L"pubDate")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item guid."); + + hr = TimeFromString(bstrNodeValue, &pItem->ftPublished); + RssExitOnFailure(hr, "Failed to convert RSS item time."); + } + else if (0 == lstrcmpW(bstrNodeName, L"enclosure")) + { + hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item enclosure url."); + + hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); + ReleaseNullBSTR(bstrNodeValue); + + hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize); + RssExitOnFailure(hr, "Failed to get RSS item enclosure length."); + + hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item enclosure type."); + + hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); + } + else + { + hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements); + RssExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeValue); + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + +LExit: + ReleaseBSTR(bstrNodeValue); + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseRssUnknownElement( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement + ) +{ + Assert(ppUnknownElement); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + RSS_UNKNOWN_ELEMENT* pNewUnknownElement; + + pNewUnknownElement = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE)); + RssExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + RssExitOnFailure(hr, "Failed to get unknown element namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + RssExitOnFailure(hr, "Failed to get unknown element name."); + + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get unknown element value."); + + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element value."); + + hr = pNode->get_attributes(&pixnnmAttributes); + RssExitOnFailure(hr, "Failed get attributes on RSS unknown element."); + + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) + { + hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); + RssExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); + + ReleaseNullObject(pixnAttribute); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + RssExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); + + RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownElement; + pNewUnknownElement = NULL; + +LExit: + FreeRssUnknownElementList(pNewUnknownElement); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + + return hr; +} + + +/******************************************************************** + ParseRssUnknownAttribute - parses out attribute from an unknown element + +*********************************************************************/ +static HRESULT ParseRssUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ) +{ + Assert(ppUnknownAttribute); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; + + pNewUnknownAttribute = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE)); + RssExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + RssExitOnFailure(hr, "Failed to get unknown attribute namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + RssExitOnFailure(hr, "Failed to get unknown attribute name."); + + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get unknown attribute value."); + + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); + + RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownAttribute; + pNewUnknownAttribute = NULL; + +LExit: + FreeRssUnknownAttributeList(pNewUnknownAttribute); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + + return hr; +} + + +/******************************************************************** + FreeRssUnknownElement - releases all of the memory used by a list of unknown elements + +*********************************************************************/ +static void FreeRssUnknownElementList( + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement + ) +{ + while (pUnknownElement) + { + RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement; + pUnknownElement = pUnknownElement->pNext; + + FreeRssUnknownAttributeList(pFree->pAttributes); + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzElement); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} + + +/******************************************************************** + FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes + +*********************************************************************/ +static void FreeRssUnknownAttributeList( + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ) +{ + while (pUnknownAttribute) + { + RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; + pUnknownAttribute = pUnknownAttribute->pNext; + + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzAttribute); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include "sceutil.h" + +// Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB. +#define MAX_SQLCE_DATABASE_SIZE 4091 + +// In case of some older versions of sqlce_oledb.h don't have these definitions, define some types. +#ifndef DBTYPEFOR_DBLENGTH +#ifdef _WIN64 +#define SKIP_SCE_COMPILE +#else +#define SCE_32BIT_ONLY +typedef DWORD DBLENGTH; +typedef LONG DBROWOFFSET; +typedef LONG DBROWCOUNT; +typedef DWORD DBCOUNTITEM; +typedef DWORD DBORDINAL; +typedef LONG DB_LORDINAL; +typedef DWORD DBBKMARK; +typedef DWORD DBBYTEOFFSET; +typedef DWORD DBREFCOUNT; +typedef DWORD DB_UPARAMS; +typedef LONG DB_LPARAMS; +typedef DWORD DBHASHVALUE; +typedef DWORD DB_DWRESERVE; +typedef LONG DB_LRESERVE; +typedef DWORD DB_URESERVE; +#endif + +#endif + +#ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit + +// structs +struct SCE_DATABASE_INTERNAL +{ + // In case we call DllGetClassObject on a specific file + HMODULE hSqlCeDll; + + volatile LONG dwTransactionRefcount; + IDBInitialize *pIDBInitialize; + IDBCreateSession *pIDBCreateSession; + ITransactionLocal *pITransactionLocal; + IDBProperties *pIDBProperties; + IOpenRowset *pIOpenRowset; + ISessionProperties *pISessionProperties; + + BOOL fChanges; // This database has changed + BOOL fPendingChanges; // Some changes are pending, upon transaction commit + BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back + BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail + + // If the database was opened as read-only, we copied it here - so delete it on close + LPWSTR sczTempDbFile; +}; + +struct SCE_ROW +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal; + + SCE_TABLE_SCHEMA *pTableSchema; + IRowset *pIRowset; + HROW hRow; + BOOL fInserting; + + DWORD dwBindingIndex; + DBBINDING *rgBinding; + SIZE_T cbOffset; + BYTE *pbData; +}; + +struct SCE_QUERY +{ + SCE_TABLE_SCHEMA *pTableSchema; + SCE_INDEX_SCHEMA *pIndexSchema; + SCE_DATABASE_INTERNAL *pDatabaseInternal; + + // Accessor build-up members + DWORD dwBindingIndex; + DBBINDING *rgBinding; + SIZE_T cbOffset; + BYTE *pbData; +}; + +struct SCE_QUERY_RESULTS +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal; + IRowset *pIRowset; + SCE_TABLE_SCHEMA *pTableSchema; +}; + +extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW); +extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY); +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS); + +// The following is the internal Sce-maintained table to tell the identifier and version of the schema +const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] = +{ + { + L"AppIdentifier", + DBTYPE_WSTR, + 0, + FALSE, + TRUE, + FALSE, + NULL, + 0, + 0 + }, + { + L"Version", + DBTYPE_I4, + 0, + FALSE, + FALSE, + FALSE, + NULL, + 0, + 0 + } +}; + +const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] = +{ + L"SceSchemaTablev1", + _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA), + (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA, + 0, + NULL, + NULL, + NULL +}; + +// internal function declarations + +// Creates an instance of SQL Server CE object, returning IDBInitialize object +// If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject +// on that file specifically. Otherwise it calls CoCreateInstance +static HRESULT CreateSqlCe( + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out IDBInitialize **ppIDBInitialize, + __out_opt HMODULE *phSqlCeDll + ); +static HRESULT RunQuery( + __in BOOL fRange, + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, + __out SCE_QUERY_RESULTS **ppsqrhHandle + ); +static HRESULT FillOutColumnDescFromSchema( + __in const SCE_COLUMN_SCHEMA *pSchema, + __out DBCOLUMNDESC pColumnDesc + ); +static HRESULT EnsureSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ); +static HRESULT OpenSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ); +static HRESULT SetColumnValue( + __in const SCE_TABLE_SCHEMA *pTableSchema, + __in DWORD dwColumnIndex, + __in_bcount_opt(cbSize) const BYTE *pbData, + __in SIZE_T cbSize, + __inout DBBINDING *pBinding, + __inout SIZE_T *pcbOffset, + __inout BYTE **ppbBuffer + ); +static HRESULT GetColumnValue( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbData, + __out SIZE_T *pcbSize + ); +static HRESULT GetColumnValueFixed( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __in DWORD cbSize, + __out BYTE *pbData + ); +static HRESULT EnsureLocalColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema + ); +static HRESULT EnsureForeignColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ); +static HRESULT SetSessionProperties( + __in ISessionProperties *pISessionProperties + ); +static HRESULT GetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __out LPWSTR *psczSchemaType, + __out DWORD *pdwVersion + ); +static HRESULT SetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __in LPCWSTR wzSchemaType, + __in DWORD dwVersion + ); +static void ReleaseDatabase( + SCE_DATABASE *pDatabase + ); +static void ReleaseDatabaseInternal( + SCE_DATABASE_INTERNAL *pDatabaseInternal + ); + +// function definitions +extern "C" HRESULT DAPI SceCreateDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out SCE_DATABASE **ppDatabase + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDirectory = NULL; + SCE_DATABASE *pNewSceDatabase = NULL; + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; + IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL; + DBPROPSET rgdbpDataSourcePropSet[2] = { }; + DBPROP rgdbpDataSourceProp[2] = { }; + DBPROP rgdbpDataSourceSsceProp[1] = { }; + + pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); + + pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); + + pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); + + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast(&pIDBDataSourceAdmin)); + ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface"); + + hr = PathGetDirectory(sczFile, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile); + + hr = DirEnsureExists(sczDirectory, NULL); + ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory); + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile); + + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[1].vValue.vt = VT_I4; + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; + + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); + + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE; + + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); + + hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL); + ExitOnFailure(hr, "Failed to create data source"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); + ExitOnFailure(hr, "Failed to get IDBProperties interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); + ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); + + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); + ExitOnFailure(hr, "Failed to get ISessionProperties interface"); + + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); + ExitOnFailure(hr, "Failed to set session properties"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); + ExitOnFailure(hr, "Failed to get IOpenRowset interface"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); + ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); + + *ppDatabase = pNewSceDatabase; + pNewSceDatabase = NULL; + +LExit: + ReleaseStr(sczDirectory); + ReleaseObject(pIDBDataSourceAdmin); + ReleaseDatabase(pNewSceDatabase); + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); + + return hr; +} + +extern "C" HRESULT DAPI SceOpenDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzExpectedSchemaType, + __in DWORD dwExpectedVersion, + __out SCE_DATABASE **ppDatabase, + __in BOOL fReadOnly + ) +{ + HRESULT hr = S_OK; + DWORD dwVersionFound = 0; + WCHAR wzTempDbFile[MAX_PATH]; + LPCWSTR wzPathToOpen = NULL; + LPWSTR sczSchemaType = NULL; + SCE_DATABASE *pNewSceDatabase = NULL; + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; + DBPROPSET rgdbpDataSourcePropSet[2] = { }; + DBPROP rgdbpDataSourceProp[2] = { }; + DBPROP rgdbpDataSourceSsceProp[1] = { }; + + pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); + + pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); + + pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); + + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); + ExitOnFailure(hr, "Failed to get IDBProperties interface"); + + // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now. + if (fReadOnly) + { + hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile)); + ExitOnFailure(hr, "Failed to get temp path"); + + hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE); + ExitOnFailure(hr, "Failed to copy file to temp path"); + + hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0); + ExitOnFailure(hr, "Failed to copy temp db file path"); + + wzPathToOpen = (LPCWSTR)wzTempDbFile; + } + else + { + wzPathToOpen = sczFile; + } + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen); + + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[1].vValue.vt = VT_I4; + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; + + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); + + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE; + + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); + + hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet); + ExitOnFailure(hr, "Failed to set initial properties to open database"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize(); + ExitOnFailure(hr, "Failed to open database: %ls", sczFile); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); + ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); + + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); + ExitOnFailure(hr, "Failed to get ISessionProperties interface"); + + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); + ExitOnFailure(hr, "Failed to set session properties"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); + ExitOnFailure(hr, "Failed to get IOpenRowset interface"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); + ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); + + hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound); + ExitOnFailure(hr, "Failed to find schema version of database"); + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1)) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE); + ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType); + } + else if (dwVersionFound != dwExpectedVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); + ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound); + } + + *ppDatabase = pNewSceDatabase; + pNewSceDatabase = NULL; + +LExit: + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); + ReleaseStr(sczSchemaType); + ReleaseDatabase(pNewSceDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceEnsureDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __in SCE_DATABASE_SCHEMA *pdsSchema, + __out SCE_DATABASE **ppDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE *pDatabase = NULL; + + if (FileExistsEx(sczFile, NULL)) + { + hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE); + ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile); + } + else + { + hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase); + ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile); + + hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion); + ExitOnFailure(hr, "Failed to set schema version of database"); + } + + hr = EnsureSchema(pDatabase, pdsSchema); + ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile); + + // Keep a pointer to the schema in the SCE_DATABASE object for future reference + pDatabase->pdsSchema = pdsSchema; + + *ppDatabase = pDatabase; + pDatabase = NULL; + +LExit: + ReleaseDatabase(pDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceIsTableEmpty( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out BOOL *pfEmpty + ) +{ + HRESULT hr = S_OK; + SCE_ROW_HANDLE row = NULL; + + hr = SceGetFirstRow(pDatabase, dwTableIndex, &row); + if (E_NOTFOUND == hr) + { + *pfEmpty = TRUE; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to get first row while checking if table is empty"); + + *pfEmpty = FALSE; + +LExit: + ReleaseSceRow(row); + + return hr; +} + +extern "C" HRESULT DAPI SceGetFirstRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + DBCOUNTITEM cRowsObtained = 0; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + SCE_ROW *pRow = NULL; + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + + hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER); + ExitOnFailure(hr, "Failed to reset IRowset position to beginning"); + + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = hRow; + pRow->pTableSchema = pTable; + pRow->pIRowset = pTable->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + +LExit: + return hr; +} + +HRESULT DAPI SceGetNextRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + DBCOUNTITEM cRowsObtained = 0; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + SCE_ROW *pRow = NULL; + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = hRow; + pRow->pTableSchema = pTable; + pRow->pIRowset = pTable->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceBeginTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + if (pDatabaseInternal->fTransactionBadState) + { + // We're in a hosed transaction state - we can't start a new one + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE)); + } + + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); + + if (1 == pDatabaseInternal->dwTransactionRefcount) + { + hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); + ExitOnFailure(hr, "Failed to start transaction"); + } + +LExit: + if (FAILED(hr)) + { + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + } + + return hr; +} + +extern "C" HRESULT DAPI SceCommitTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + Assert(0 < pDatabaseInternal->dwTransactionRefcount); + + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + + if (0 == pDatabaseInternal->dwTransactionRefcount) + { + if (pDatabaseInternal->fRollbackTransaction) + { + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); + ExitOnFailure(hr, "Failed to abort transaction"); + } + else + { + hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0); + ExitOnFailure(hr, "Failed to commit transaction"); + } + + if (pDatabaseInternal->fPendingChanges) + { + pDatabaseInternal->fPendingChanges = FALSE; + pDatabaseInternal->fChanges = TRUE; + } + + pDatabaseInternal->fRollbackTransaction = FALSE; + } + +LExit: + // If we tried to commit and failed, the caller should subsequently call rollback + if (FAILED(hr)) + { + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); + } + + return hr; +} + +extern "C" HRESULT DAPI SceRollbackTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + Assert(0 < pDatabaseInternal->dwTransactionRefcount); + + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + + if (0 == pDatabaseInternal->dwTransactionRefcount) + { + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); + ExitOnFailure(hr, "Failed to abort transaction"); + pDatabaseInternal->fPendingChanges = FALSE; + + pDatabaseInternal->fRollbackTransaction = FALSE; + } + else + { + pDatabaseInternal->fRollbackTransaction = TRUE; + } + +LExit: + // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?) + // but mark the database as bad so the user gets an error if they try to start a new transaction. + if (FAILED(hr)) + { + TraceError(hr, "Failed to rollback transaction"); + pDatabaseInternal->fTransactionBadState = TRUE; + } + + return hr; +} + +extern "C" HRESULT DAPI SceDeleteRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(*pRowHandle); + IRowsetChange *pIRowsetChange = NULL; + DBROWSTATUS rowStatus = DBROWSTATUS_S_OK; + + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange interface"); + + hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus); + ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus); + + ReleaseNullSceRow(*pRowHandle); + +LExit: + ReleaseObject(pIRowsetChange); + + return hr; +} + +extern "C" HRESULT DAPI ScePrepareInsert( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = NULL; + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = DB_NULL_HROW; + pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + pRow->pIRowset = pRow->pTableSchema->pIRowset; + pRow->pIRowset->AddRef(); + pRow->fInserting = TRUE; + + *pRowHandle = reinterpret_cast(pRow); + pRow = NULL; + +LExit: + ReleaseSceRow(pRow); + + return hr; +} + +extern "C" HRESULT DAPI SceFinishUpdate( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + IAccessor *pIAccessor = NULL; + IRowsetChange *pIRowsetChange = NULL; + DBBINDSTATUS *rgBindStatus = NULL; + HACCESSOR hAccessor = DB_NULL_HACCESSOR; + HROW hRow = DB_NULL_HROW; + + hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + +// This can be used when stepping through the debugger to see bind failures +#ifdef DEBUG + if (0 < pRow->dwBindingIndex) + { + hr = MemEnsureArraySize(reinterpret_cast(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0); + ExitOnFailure(hr, "Failed to ensure binding status array size"); + } +#endif + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange interface"); + + if (pRow->fInserting) + { + hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow); + ExitOnFailure(hr, "Failed to insert new row"); + + pRow->hRow = hRow; + ReleaseNullObject(pRow->pIRowset); + pRow->pIRowset = pRow->pTableSchema->pIRowset; + pRow->pIRowset->AddRef(); + } + else + { + hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData); + ExitOnFailure(hr, "Failed to update existing row"); + } + + if (0 < pRow->pDatabaseInternal->dwTransactionRefcount) + { + pRow->pDatabaseInternal->fPendingChanges = TRUE; + } + else + { + pRow->pDatabaseInternal->fChanges = TRUE; + } + +LExit: + if (DB_NULL_HACCESSOR != hAccessor) + { + pIAccessor->ReleaseAccessor(hAccessor, NULL); + } + ReleaseMem(rgBindStatus); + ReleaseObject(pIAccessor); + ReleaseObject(pIRowsetChange); + + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD dwValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as qword"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const BOOL fValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + short int sValue = fValue ? 0xFFFF : 0x0000; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR)); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnNull( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as empty value"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + DBTIMESTAMP dbTimeStamp = { }; + + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + dbTimeStamp.year = pst->wYear; + dbTimeStamp.month = pst->wMonth; + dbTimeStamp.day = pst->wDay; + dbTimeStamp.hour = pst->wHour; + dbTimeStamp.minute = pst->wMinute; + dbTimeStamp.second = pst->wSecond; + // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second, + // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds + // are involved (even when rounded the way SQL CE returns them). + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbBuffer, + __inout SIZE_T *pcbBuffer + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get binary data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD *pdwValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast(pdwValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get dword data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __in DWORD64 *pqwValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast(pqwValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get qword data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out BOOL *pfValue + ) +{ + HRESULT hr = S_OK; + short int sValue = 0; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast(&sValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get data out of column"); + + if (sValue == 0x0000) + { + *pfValue = FALSE; + } + else + { + *pfValue = TRUE; + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_z LPWSTR *psczValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + SIZE_T cbSize = 0; + + hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast(psczValue), &cbSize); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get string data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + DBTIMESTAMP dbTimeStamp = { }; + + hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast(&dbTimeStamp)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get string data out of column"); + + pst->wYear = dbTimeStamp.year; + pst->wMonth = dbTimeStamp.month; + pst->wDay = dbTimeStamp.day; + pst->wHour = dbTimeStamp.hour; + pst->wMinute = dbTimeStamp.minute; + pst->wSecond = dbTimeStamp.second; + +LExit: + return hr; +} + +extern "C" void DAPI SceCloseTable( + __in SCE_TABLE_SCHEMA *pTable + ) +{ + ReleaseNullObject(pTable->pIRowsetChange); + ReleaseNullObject(pTable->pIRowset); +} + +extern "C" BOOL DAPI SceDatabaseChanged( + __in SCE_DATABASE *pDatabase + ) +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + return pDatabaseInternal->fChanges; +} + +void DAPI SceResetDatabaseChanged( + __in SCE_DATABASE *pDatabase + ) +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + pDatabaseInternal->fChanges = FALSE; +} + +extern "C" HRESULT DAPI SceCloseDatabase( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + + ReleaseDatabase(pDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceBeginQuery( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __in DWORD dwIndex, + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_QUERY *psq = static_cast(MemAlloc(sizeof(SCE_QUERY), TRUE)); + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query"); + + psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]); + psq->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns); + + psq->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query"); + + *psqhHandle = static_cast(psq); + psq = NULL; + +LExit: + ReleaseSceQuery(psq); + + return hr; +} + +HRESULT DAPI SceSetQueryColumnBinary( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnDword( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD dwValue + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as dword"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnQword( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as qword"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnBool( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const BOOL fValue + ) +{ + HRESULT hr = S_OK; + short int sValue = fValue ? 1 : 0; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as boolean"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnString( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_z_opt LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR)); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as string"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnSystemTime( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + DBTIMESTAMP dbTimeStamp = { }; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + dbTimeStamp.year = pst->wYear; + dbTimeStamp.month = pst->wMonth; + dbTimeStamp.day = pst->wDay; + dbTimeStamp.hour = pst->wHour; + dbTimeStamp.minute = pst->wMinute; + dbTimeStamp.second = pst->wSecond; + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnEmpty( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as empty value"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceRunQueryExact( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY_RESULTS *pQueryResults = NULL; + + hr = RunQuery(FALSE, *psqhHandle, &pQueryResults); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to run query exact"); + + hr = SceGetNextResultRow(reinterpret_cast(pQueryResults), pRowHandle); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get next row out of results"); + +LExit: + ReleaseNullSceQuery(*psqhHandle); + ReleaseSceQueryResults(pQueryResults); + + return hr; +} + +extern "C" HRESULT DAPI SceRunQueryRange( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast(psqrhHandle); + + hr = RunQuery(TRUE, *psqhHandle, ppQueryResults); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to run query for range"); + +LExit: + ReleaseNullSceQuery(*psqhHandle); + + return hr; +} + +extern "C" HRESULT DAPI SceGetNextResultRow( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + DBCOUNTITEM cRowsObtained = 0; + SCE_ROW *pRow = NULL; + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); + + Assert(pRowHandle && (*pRowHandle == NULL)); + + hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pQueryResults->pDatabaseInternal); + pRow->hRow = hRow; + pRow->pTableSchema = pQueryResults->pTableSchema; + pRow->pIRowset = pQueryResults->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + pRow = NULL; + hRow = DB_NULL_HROW; + +LExit: + if (DB_NULL_HROW != hRow) + { + pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); + } + ReleaseSceRow(pRow); + + return hr; +} + +extern "C" void DAPI SceFreeRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ) +{ + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + if (DB_NULL_HROW != pRow->hRow) + { + pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL); + } + ReleaseObject(pRow->pIRowset); + ReleaseMem(pRow->rgBinding); + ReleaseMem(pRow->pbData); + ReleaseMem(pRow); +} + +void DAPI SceFreeQuery( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle + ) +{ + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + ReleaseMem(pQuery->rgBinding); + ReleaseMem(pQuery->pbData); + ReleaseMem(pQuery); +} + +void DAPI SceFreeQueryResults( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle + ) +{ + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); + + ReleaseObject(pQueryResults->pIRowset); + ReleaseMem(pQueryResults); +} + +// internal function definitions +static HRESULT CreateSqlCe( + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out IDBInitialize **ppIDBInitialize, + __out_opt HMODULE *phSqlCeDll + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + LPWSTR sczDirectory = NULL; + LPWSTR sczDllFullPath = NULL; + + if (NULL == wzSqlCeDllPath) + { + hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast(ppIDBInitialize)); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + } + else + { + // First try loading DLL from the path of our EXE + hr = PathForCurrentProcess(&sczPath, NULL); + ExitOnFailure(hr, "Failed to get path for current process"); + + hr = PathGetDirectory(sczPath, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory of current process"); + + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); + + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); + + // If that failed, fallback to loading from current path + if (NULL == *phSqlCeDll) + { + hr = DirGetCurrent(&sczDirectory); + ExitOnFailure(hr, "Failed to get current directory"); + + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); + + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); + ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath); + } + + HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**); + pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject"); + + IClassFactory* pFactory = NULL; + hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory); + ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath); + ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL"); + + hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize); + pFactory->Release(); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczDirectory); + ReleaseStr(sczDllFullPath); + + return hr; +} + +static HRESULT RunQuery( + __in BOOL fRange, + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults + ) +{ + HRESULT hr = S_OK; + DBID tableID = { }; + DBID indexID = { }; + IAccessor *pIAccessor = NULL; + IRowsetIndex *pIRowsetIndex = NULL; + IRowset *pIRowset = NULL; + HACCESSOR hAccessor = DB_NULL_HACCESSOR; + SCE_QUERY *pQuery = reinterpret_cast(psqhHandle); + SCE_QUERY_RESULTS *pQueryResults = NULL; + DBPROPSET rgdbpIndexPropSet[1]; + DBPROP rgdbpIndexProp[1]; + + rgdbpIndexPropSet[0].cProperties = 1; + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; + + rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex; + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpIndexProp[0].colid = DB_NULLID; + rgdbpIndexProp[0].vValue.vt = VT_BOOL; + rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE; + + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pQuery->pTableSchema->wzName); + + indexID.eKind = DBKIND_NAME; + indexID.uName.pwszName = const_cast(pQuery->pIndexSchema->wzName); + + hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex); + ExitOnFailure(hr, "Failed to open IRowsetIndex"); + + hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast(&pIRowset)); + ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex"); + + hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL); + ExitOnFailure(hr, "Failed to create accessor"); + + if (!fRange) + { + hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ); + if (DB_E_NOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to seek to record"); + } + else + { + // If ALL columns in the index were specified, do a full key match + if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns) + { + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH); + } + else + { + // Otherwise, just match the specified keys. + // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it + // So instead, we set the start and end to the same partial key, and then allow inclusive matching + // This appears to accomplish the same thing + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0); + } + if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to set range to find records"); + } + + pQueryResults = reinterpret_cast(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE)); + ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct"); + + pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal; + pQueryResults->pTableSchema = pQuery->pTableSchema; + pQueryResults->pIRowset = pIRowset; + pIRowset = NULL; + + *ppQueryResults = pQueryResults; + pQueryResults = NULL; + +LExit: + if (DB_NULL_HACCESSOR != hAccessor) + { + pIAccessor->ReleaseAccessor(hAccessor, NULL); + } + ReleaseObject(pIAccessor); + ReleaseObject(pIRowset); + ReleaseObject(pIRowsetIndex); + ReleaseMem(pQueryResults); + ReleaseSceQueryResults(pQueryResults); + + return hr; +} + +static HRESULT FillOutColumnDescFromSchema( + __in const SCE_COLUMN_SCHEMA *pColumnSchema, + __out DBCOLUMNDESC *pColumnDesc + ) +{ + HRESULT hr = S_OK; + DWORD dwColumnProperties = 0; + DWORD dwColumnPropertyIndex = 0; + BOOL fFixedSize = FALSE; + + pColumnDesc->dbcid.eKind = DBKIND_NAME; + pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName; + pColumnDesc->wType = pColumnSchema->dbtColumnType; + pColumnDesc->ulColumnSize = pColumnSchema->dwLength; + if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType)) + { + fFixedSize = FALSE; + } + else + { + fFixedSize = TRUE; + } + + dwColumnProperties = 1; + if (pColumnSchema->fAutoIncrement) + { + ++dwColumnProperties; + } + if (!pColumnSchema->fNullable) + { + ++dwColumnProperties; + } + + if (0 < dwColumnProperties) + { + pColumnDesc->cPropertySets = 1; + pColumnDesc->rgPropertySets = reinterpret_cast(MemAlloc(sizeof(DBPROPSET), TRUE)); + ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters"); + + pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties; + pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN; + pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE)); + + dwColumnPropertyIndex = 0; + if (pColumnSchema->fAutoIncrement) + { + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE; + ++dwColumnPropertyIndex; + } + if (!pColumnSchema->fNullable) + { + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE; + ++dwColumnPropertyIndex; + } + + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE; + ++dwColumnPropertyIndex; + } + +LExit: + return hr; +} + +static HRESULT EnsureSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + BOOL fInTransaction = FALSE; + BOOL fSchemaNeedsSetup = TRUE; + DBID tableID = { }; + DBID indexID = { }; + DBPROPSET rgdbpIndexPropSet[1]; + DBPROPSET rgdbpRowSetPropSet[1]; + DBPROP rgdbpIndexProp[1]; + DBPROP rgdbpRowSetProp[1]; + DBCOLUMNDESC *rgColumnDescriptions = NULL; + DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL; + DWORD cIndexColumnDescriptions = 0; + DWORD dwTableColumnIndex = 0; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + ITableDefinition *pTableDefinition = NULL; + IIndexDefinition *pIndexDefinition = NULL; + IRowsetIndex *pIRowsetIndex = NULL; + + rgdbpRowSetPropSet[0].cProperties = 1; + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; + + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpRowSetProp[0].colid = DB_NULLID; + rgdbpRowSetProp[0].vValue.vt = VT_BOOL; + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; + + rgdbpIndexPropSet[0].cProperties = 1; + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX; + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; + + rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS; + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpIndexProp[0].colid = DB_NULLID; + rgdbpIndexProp[0].vValue.vt = VT_I4; + rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL; + + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast(&pTableDefinition)); + ExitOnFailure(hr, "Failed to get ITableDefinition for table creation"); + + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast(&pIndexDefinition)); + ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation"); + + hr = SceBeginTransaction(pDatabase); + ExitOnFailure(hr, "Failed to start transaction for ensuring schema"); + fInTransaction = TRUE; + + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist + rgColumnDescriptions = static_cast(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE)); + ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table"); + + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i); + ExitOnFailure(hr, "Failed to fill out column description from schema"); + } + + // First try to open the table - or if it doesn't exist, create it + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); + if (DB_E_NOTABLE == hr) + { + // The table doesn't exist, so let's create it + hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL); + ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + else + { + ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName); + + // Close any rowset we opened + ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset); + + // If it does exist, make sure all columns are in the table + // Only nullable columns can be added to an existing table + for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable) + hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL); + if (DB_E_DUPLICATECOLUMNID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName); + } + } + +#pragma prefast(push) +#pragma prefast(disable:26010) + hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable); +#pragma prefast(pop) + ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); + + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + if (NULL != rgColumnDescriptions[i].rgPropertySets) + { + ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties); + ReleaseMem(rgColumnDescriptions[i].rgPropertySets); + } + } + + ReleaseNullMem(rgColumnDescriptions); + if (0 < pdsSchema->rgTables[dwTable].cIndexes) + { + // Now create indexes for the table + for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex) + { + indexID.eKind = DBKIND_NAME; + indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName; + + // Check if the index exists + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex); + if (SUCCEEDED(hr)) + { + // TODO: If one with the same name exists, check if the schema actually matches + ReleaseNullObject(pIRowsetIndex); + continue; + } + hr = S_OK; + + hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns); + + rgIndexColumnDescriptions = reinterpret_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions"); + cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns; + + for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex) + { + dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex]; + + rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast(MemAlloc(sizeof(DBID), TRUE)); + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME; + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName); + rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC; + } + + hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL); + if (DB_E_DUPLICATEINDEXID == hr) + { + // If the index already exists, no worries + hr = S_OK; + } + ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName); + + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) + { + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); + } + + cIndexColumnDescriptions = 0; + ReleaseNullMem(rgIndexColumnDescriptions); + } + } + } + + // Now once all tables have been created, create foreign key relationships + if (fSchemaNeedsSetup) + { + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // Setup any constraints for the table's columns + hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema); + ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + } + + hr = SceCommitTransaction(pDatabase); + ExitOnFailure(hr, "Failed to commit transaction for ensuring schema"); + fInTransaction = FALSE; + + hr = OpenSchema(pDatabase, pdsSchema); + ExitOnFailure(hr, "Failed to open schema"); + +LExit: + ReleaseObject(pTableDefinition); + ReleaseObject(pIndexDefinition); + ReleaseObject(pIRowsetIndex); + + if (fInTransaction) + { + SceRollbackTransaction(pDatabase); + } + + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) + { + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); + } + + ReleaseMem(rgIndexColumnDescriptions); + ReleaseMem(rgColumnDescriptions); + + return hr; +} + +static HRESULT OpenSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + DBID tableID = { }; + DBPROPSET rgdbpRowSetPropSet[1]; + DBPROP rgdbpRowSetProp[1]; + + rgdbpRowSetPropSet[0].cProperties = 1; + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; + + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpRowSetProp[0].colid = DB_NULLID; + rgdbpRowSetProp[0].vValue.vt = VT_BOOL; + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; + + // Finally, open all tables + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // And finally, open the table's standard interfaces + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); + ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName); + + hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + +LExit: + return hr; +} + +static HRESULT SetColumnValue( + __in const SCE_TABLE_SCHEMA *pTableSchema, + __in DWORD dwColumnIndex, + __in_bcount_opt(cbSize) const BYTE *pbData, + __in SIZE_T cbSize, + __inout DBBINDING *pBinding, + __inout SIZE_T *pcbOffset, + __inout BYTE **ppbBuffer + ) +{ + HRESULT hr = S_OK; + size_t cbNewOffset = *pcbOffset; + + pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column + pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; + pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; + + pBinding->obLength = cbNewOffset; + + hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset); + ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value"); + + pBinding->obValue = cbNewOffset; + + hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset); + ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize); + + pBinding->obStatus = cbNewOffset; + pBinding->eParamIO = DBPARAMIO_INPUT; + + hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset); + ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value"); + + pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType; + pBinding->cbMaxLen = static_cast(cbSize); + + if (NULL == *ppbBuffer) + { + *ppbBuffer = reinterpret_cast(MemAlloc(cbNewOffset, TRUE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string"); + } + else + { + *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string"); + } + + *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = static_cast(cbSize); + *pcbOffset += sizeof(DBBYTEOFFSET); + memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize); + *pcbOffset += cbSize; + if (NULL == pbData) + { + *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL; + } + *pcbOffset += sizeof(DBSTATUS); + +LExit: + return hr; +} + +static HRESULT GetColumnValue( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbData, + __out SIZE_T *pcbSize + ) +{ + HRESULT hr = S_OK; + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; + IAccessor *pIAccessor = NULL; + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; + DWORD dwDataSize = 0; + void *pvRawData = NULL; + DBBINDING dbBinding = { }; + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; + + dbBinding.iOrdinal = dwColumnIndex + 1; + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; + dbBinding.dwPart = DBPART_LENGTH; + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; + + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); + ExitOnFailure(hr, "Failed to get size of data"); + + // For variable-length columns, zero data returned means NULL + if (0 == dwDataSize) + { + ExitFunction1(hr = E_NOTFOUND); + } + + if (NULL != ppbData) + { + dbBinding.dwPart = DBPART_VALUE; + dbBinding.cbMaxLen = dwDataSize; + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); + } + + if (DBTYPE_WSTR == dbBinding.wType) + { + hr = StrAlloc(reinterpret_cast(&pvRawData), dwDataSize / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex); + } + else + { + pvRawData = MemAlloc(dwDataSize, TRUE); + ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData); + ExitOnFailure(hr, "Failed to read data value"); + + ReleaseMem(*ppbData); + *ppbData = reinterpret_cast(pvRawData); + pvRawData = NULL; + } + + *pcbSize = dwDataSize; + +LExit: + ReleaseMem(pvRawData); + + if (DB_NULL_HACCESSOR != hAccessorLength) + { + pIAccessor->ReleaseAccessor(hAccessorLength, NULL); + } + if (DB_NULL_HACCESSOR != hAccessorValue) + { + pIAccessor->ReleaseAccessor(hAccessorValue, NULL); + } + ReleaseObject(pIAccessor); + + return hr; +} + +static HRESULT GetColumnValueFixed( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __in DWORD cbSize, + __out BYTE *pbData + ) +{ + HRESULT hr = S_OK; + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; + IAccessor *pIAccessor = NULL; + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; + DWORD dwDataSize = 0; + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; + DBBINDING dbBinding = { }; + + dbBinding.iOrdinal = dwColumnIndex + 1; + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; + dbBinding.dwPart = DBPART_LENGTH; + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; + + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value"); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); + ExitOnFailure(hr, "Failed to get size of data"); + + if (0 == dwDataSize) + { + ExitFunction1(hr = E_NOTFOUND); + } + + dbBinding.dwPart = DBPART_VALUE; + dbBinding.cbMaxLen = cbSize; + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast(pbData)); + ExitOnFailure(hr, "Failed to read data value"); + +LExit: + if (DB_NULL_HACCESSOR != hAccessorLength) + { + pIAccessor->ReleaseAccessor(hAccessorLength, NULL); + } + if (DB_NULL_HACCESSOR != hAccessorValue) + { + pIAccessor->ReleaseAccessor(hAccessorValue, NULL); + } + ReleaseObject(pIAccessor); + + return hr; +} + +static HRESULT EnsureLocalColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema + ) +{ + HRESULT hr = S_OK; + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; + DBCONSTRAINTDESC dbcdConstraint = { }; + DBID dbConstraintID = { }; + DBID dbLocalColumnID = { }; + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; + + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); + + for (DWORD i = 0; i < pTableSchema->cColumns; ++i) + { + pCurrentColumn = pTableSchema->rgColumns + i; + + // Add a primary key constraint for this column, if one exists + if (pCurrentColumn->fPrimaryKey) + { + // Setup DBID for new constraint + dbConstraintID.eKind = DBKIND_NAME; + dbConstraintID.uName.pwszName = const_cast(L"PrimaryKey"); + dbcdConstraint.pConstraintID = &dbConstraintID; + + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY; + + dbLocalColumnID.eKind = DBKIND_NAME; + dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); + dbcdConstraint.cColumns = 1; + dbcdConstraint.rgColumnList = &dbLocalColumnID; + + dbcdConstraint.pReferencedTableID = NULL; + dbcdConstraint.cForeignKeyColumns = 0; + dbcdConstraint.rgForeignKeyColumnList = NULL; + dbcdConstraint.pwszConstraintText = NULL; + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; + dbcdConstraint.MatchType = DBMATCHTYPE_NONE; + dbcdConstraint.Deferrability = 0; + dbcdConstraint.cReserved = 0; + dbcdConstraint.rgReserved = NULL; + + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); + if (DB_E_DUPLICATECONSTRAINTID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName); + } + } + +LExit: + ReleaseObject(pTableDefinitionWithConstraints); + + return hr; +} + +static HRESULT EnsureForeignColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ) +{ + HRESULT hr = S_OK; + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; + DBCONSTRAINTDESC dbcdConstraint = { }; + DBID dbConstraintID = { }; + DBID dbLocalColumnID = { }; + DBID dbForeignTableID = { }; + DBID dbForeignColumnID = { }; + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; + + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); + + for (DWORD i = 0; i < pTableSchema->cColumns; ++i) + { + pCurrentColumn = pTableSchema->rgColumns + i; + + // Add a foreign key constraint for this column, if one exists + if (NULL != pCurrentColumn->wzRelationName) + { + // Setup DBID for new constraint + dbConstraintID.eKind = DBKIND_NAME; + dbConstraintID.uName.pwszName = const_cast(pCurrentColumn->wzRelationName); + dbcdConstraint.pConstraintID = &dbConstraintID; + + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY; + + dbForeignColumnID.eKind = DBKIND_NAME; + dbForeignColumnID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName); + dbcdConstraint.cColumns = 1; + dbcdConstraint.rgColumnList = &dbForeignColumnID; + + dbForeignTableID.eKind = DBKIND_NAME; + dbForeignTableID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName); + dbcdConstraint.pReferencedTableID = &dbForeignTableID; + + dbLocalColumnID.eKind = DBKIND_NAME; + dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); + dbcdConstraint.cForeignKeyColumns = 1; + dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID; + + dbcdConstraint.pwszConstraintText = NULL; + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; + dbcdConstraint.MatchType = DBMATCHTYPE_FULL; + dbcdConstraint.Deferrability = 0; + dbcdConstraint.cReserved = 0; + dbcdConstraint.rgReserved = NULL; + + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); + if (DB_E_DUPLICATECONSTRAINTID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName); + } + } + +LExit: + ReleaseObject(pTableDefinitionWithConstraints); + + return hr; +} + +static HRESULT SetSessionProperties( + __in ISessionProperties *pISessionProperties + ) +{ + HRESULT hr = S_OK; + DBPROP rgdbpDataSourceProp[1]; + DBPROPSET rgdbpDataSourcePropSet[1]; + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH; + + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = 1; + + hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet); + ExitOnFailure(hr, "Failed to set session properties"); + +LExit: + return hr; +} + +static HRESULT GetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __out LPWSTR *psczSchemaType, + __out DWORD *pdwVersion + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSchemaType = NULL; + DWORD dwVersionFound = 0; + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; + // Database object with our alternate schema + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; + SCE_ROW_HANDLE sceRow = NULL; + + hr = OpenSchema(pDatabase, &fullSchema); + ExitOnFailure(hr, "Failed to ensure internal version schema"); + + hr = SceGetFirstRow(&database, 0, &sceRow); + ExitOnFailure(hr, "Failed to get first row in internal version schema table"); + + hr = SceGetColumnString(sceRow, 0, &sczSchemaType); + ExitOnFailure(hr, "Failed to get internal schematype"); + + hr = SceGetColumnDword(sceRow, 1, &dwVersionFound); + ExitOnFailure(hr, "Failed to get internal version"); + + *psczSchemaType = sczSchemaType; + sczSchemaType = NULL; + *pdwVersion = dwVersionFound; + +LExit: + SceCloseTable(&schemaTable); // ignore failure + ReleaseStr(sczSchemaType); + ReleaseSceRow(sceRow); + + return hr; +} + +static HRESULT SetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __in LPCWSTR wzSchemaType, + __in DWORD dwVersion + ) +{ + HRESULT hr = S_OK; + BOOL fInSceTransaction = FALSE; + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; + // Database object with our alternate schema + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; + SCE_ROW_HANDLE sceRow = NULL; + + hr = EnsureSchema(pDatabase, &fullSchema); + ExitOnFailure(hr, "Failed to ensure internal version schema"); + + hr = SceBeginTransaction(&database); + ExitOnFailure(hr, "Failed to begin transaction"); + fInSceTransaction = TRUE; + + hr = SceGetFirstRow(&database, 0, &sceRow); + if (E_NOTFOUND == hr) + { + hr = ScePrepareInsert(&database, 0, &sceRow); + ExitOnFailure(hr, "Failed to insert only row into internal version schema table"); + } + else + { + ExitOnFailure(hr, "Failed to get first row in internal version schema table"); + } + + hr = SceSetColumnString(sceRow, 0, wzSchemaType); + ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType); + + hr = SceSetColumnDword(sceRow, 1, dwVersion); + ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion); + + hr = SceFinishUpdate(sceRow); + ExitOnFailure(hr, "Failed to insert first row in internal version schema table"); + + hr = SceCommitTransaction(&database); + ExitOnFailure(hr, "Failed to commit transaction"); + fInSceTransaction = FALSE; + +LExit: + SceCloseTable(&schemaTable); // ignore failure + ReleaseSceRow(sceRow); + if (fInSceTransaction) + { + SceRollbackTransaction(&database); + } + + return hr; +} + +static void ReleaseDatabase( + SCE_DATABASE *pDatabase + ) +{ + if (NULL != pDatabase) + { + if (NULL != pDatabase->pdsSchema) + { + for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i) + { + SceCloseTable(pDatabase->pdsSchema->rgTables + i); + } + } + + if (NULL != pDatabase->sdbHandle) + { + ReleaseDatabaseInternal(reinterpret_cast(pDatabase->sdbHandle)); + } + } + ReleaseMem(pDatabase); +} + +static void ReleaseDatabaseInternal( + SCE_DATABASE_INTERNAL *pDatabaseInternal + ) +{ + HRESULT hr = S_OK; + + if (NULL != pDatabaseInternal) + { + ReleaseObject(pDatabaseInternal->pITransactionLocal); + ReleaseObject(pDatabaseInternal->pIOpenRowset); + ReleaseObject(pDatabaseInternal->pISessionProperties); + ReleaseObject(pDatabaseInternal->pIDBCreateSession); + ReleaseObject(pDatabaseInternal->pIDBProperties); + + if (NULL != pDatabaseInternal->pIDBInitialize) + { + hr = pDatabaseInternal->pIDBInitialize->Uninitialize(); + if (FAILED(hr)) + { + TraceError(hr, "Failed to call uninitialize on IDBInitialize"); + } + ReleaseObject(pDatabaseInternal->pIDBInitialize); + } + + if (NULL != pDatabaseInternal->hSqlCeDll) + { + if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to free sql ce dll"); + } + } + } + + // If there was a temp file we copied to (for read-only databases), delete it after close + if (NULL != pDatabaseInternal->sczTempDbFile) + { + hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile); + if (FAILED(hr)) + { + TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile); + } + ReleaseStr(pDatabaseInternal->sczTempDbFile); + } + + ReleaseMem(pDatabaseInternal); +} + +#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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__) +#define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__) + +static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ); +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ); + +/******************************************************************** + ShelFunctionOverride - overrides the shell functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI ShelFunctionOverride( + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW + ) +{ + vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW; +} + + +/******************************************************************** + ShelExec() - executes a target. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExec( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd, + __in_opt HWND hwndParent, + __out_opt HANDLE* phProcess + ) +{ + HRESULT hr = S_OK; + SHELLEXECUTEINFOW shExecInfo = {}; + + shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; + shExecInfo.hwnd = hwndParent; + shExecInfo.lpVerb = wzVerb; + shExecInfo.lpFile = wzTargetPath; + shExecInfo.lpParameters = wzParameters; + shExecInfo.lpDirectory = wzWorkingDirectory; + shExecInfo.nShow = nShowCmd; + + if (!vpfnShellExecuteExW(&shExecInfo)) + { + ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); + } + + if (phProcess) + { + *phProcess = shExecInfo.hProcess; + shExecInfo.hProcess = NULL; + } + +LExit: + ReleaseHandle(shExecInfo.hProcess); + + return hr; +} + + +/******************************************************************** + ShelExecUnelevated() - executes a target unelevated. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExecUnelevated( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd + ) +{ + HRESULT hr = S_OK; + BSTR bstrTargetPath = NULL; + VARIANT vtParameters = { }; + VARIANT vtVerb = { }; + VARIANT vtWorkingDirectory = { }; + VARIANT vtShow = { }; + IShellView* psv = NULL; + IShellDispatch2* psd = NULL; + + bstrTargetPath = ::SysAllocString(wzTargetPath); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); + + if (wzParameters && *wzParameters) + { + vtParameters.vt = VT_BSTR; + vtParameters.bstrVal = ::SysAllocString(wzParameters); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); + } + + if (wzVerb && *wzVerb) + { + vtVerb.vt = VT_BSTR; + vtVerb.bstrVal = ::SysAllocString(wzVerb); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); + } + + if (wzWorkingDirectory && *wzWorkingDirectory) + { + vtWorkingDirectory.vt = VT_BSTR; + vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); + } + + vtShow.vt = VT_INT; + vtShow.intVal = nShowCmd; + + hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); + ShelExitOnFailure(hr, "Failed to get desktop shell view."); + + hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); + ShelExitOnFailure(hr, "Failed to get shell dispatch from view."); + + hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); + } + ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); + +LExit: + ReleaseObject(psd); + ReleaseObject(psv); + ReleaseBSTR(vtWorkingDirectory.bstrVal); + ReleaseBSTR(vtVerb.bstrVal); + ReleaseBSTR(vtParameters.bstrVal); + ReleaseBSTR(bstrTargetPath); + + return hr; +} + + +/******************************************************************** + ShelGetFolder() - gets a folder by CSIDL. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ) +{ + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH]; + + hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); + ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); + + hr = StrAllocString(psczFolderPath, wzPath, 0); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + return hr; +} + + +/******************************************************************** + ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. + + Note: return E_NOTIMPL if called on pre-Vista operating systems. +*******************************************************************/ +#ifndef REFKNOWNFOLDERID +#define REFKNOWNFOLDERID REFGUID +#endif + +#ifndef KF_FLAG_CREATE +#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition +#endif + +EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( + REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath + ); + +extern "C" HRESULT DAPI ShelGetKnownFolder( + __out_z LPWSTR* psczFolderPath, + __in REFKNOWNFOLDERID rfidFolder + ) +{ + HRESULT hr = S_OK; + HMODULE hShell32Dll = NULL; + PFN_SHGetKnownFolderPath pfn = NULL; + LPWSTR pwzPath = NULL; + + hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll); + if (E_MODNOTFOUND == hr) + { + TraceError(hr, "Failed to load shell32.dll"); + ExitFunction1(hr = E_NOTIMPL); + } + ShelExitOnFailure(hr, "Failed to load shell32.dll."); + + pfn = reinterpret_cast(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); + ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); + + hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); + ShelExitOnFailure(hr, "Failed to get known folder path."); + + hr = StrAllocString(psczFolderPath, pwzPath, 0); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + if (pwzPath) + { + ::CoTaskMemFree(pwzPath); + } + + if (hShell32Dll) + { + ::FreeLibrary(hShell32Dll); + } + + return hr; +} + + +// Internal functions. + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IShellWindows* psw = NULL; + HWND hwnd = NULL; + IDispatch* pdisp = NULL; + VARIANT vEmpty = {}; // VT_EMPTY + IShellBrowser* psb = NULL; + IShellFolder* psf = NULL; + IShellView* psv = NULL; + + // use the shell view for the desktop using the shell windows automation to find the + // desktop web browser and then grabs its view + // returns IShellView, IFolderView and related interfaces + hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); + ShelExitOnFailure(hr, "Failed to get shell view."); + + hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); + if (S_OK == hr) + { + hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); + ShelExitOnFailure(hr, "Failed to get desktop window."); + + hr = psb->QueryActiveShellView(&psv); + ShelExitOnFailure(hr, "Failed to get active shell view."); + + hr = psv->QueryInterface(riid, ppv); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else if (S_FALSE == hr) + { + //Windows XP + hr = SHGetDesktopFolder(&psf); + ShelExitOnFailure(hr, "Failed to get desktop folder."); + + hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else + { + ShelExitOnFailure(hr, "Failed to get desktop window."); + } + +LExit: + ReleaseObject(psv); + ReleaseObject(psb); + ReleaseObject(psf); + ReleaseObject(pdisp); + ReleaseObject(psw); + + return hr; +} + +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IDispatch *pdispBackground = NULL; + IShellFolderViewDual *psfvd = NULL; + IDispatch *pdisp = NULL; + + // From a shell view object, gets its automation interface and from that get the shell + // application object that implements IShellDispatch2 and related interfaces. + hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); + ShelExitOnFailure(hr, "Failed to get the automation interface for shell."); + + hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); + ShelExitOnFailure(hr, "Failed to get shell folder view dual."); + + hr = psfvd->get_Application(&pdisp); + ShelExitOnFailure(hr, "Failed to application object."); + + hr = pdisp->QueryInterface(riid, ppv); + ShelExitOnFailure(hr, "Failed to get IShellDispatch2."); + +LExit: + ReleaseObject(pdisp); + ReleaseObject(psfvd); + ReleaseObject(pdispBackground); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// okay, this may look a little weird, but sqlutil.h cannot be in the +// pre-compiled header because we need to #define these things so the +// correct GUID's get pulled into this object file +#include +#define DBINITCONSTANTS +#include "sqlutil.h" + + +//Please note that only SQL native client 11 has TLS1.2 support +#define _SQLNCLI_OLEDB_DEPRECATE_WARNING + +#if !defined(SQLNCLI_VER) +#define SQLNCLI_VER 1100 +#endif + +#if SQLNCLI_VER >= 1100 +#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) +#define SQLNCLI_CLSID CLSID_SQLNCLI11 +#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) +extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } }; +#endif // SQLNCLI_VER >= 1100 + +// Exit macros +#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) +#define SqlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) +#define SqlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) +#define SqlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) +#define SqlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SQLUTIL, e, x, s, __VA_ARGS__) +#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__) + +// private prototypes +static HRESULT InitializeDatabaseConnection( + __in REFCLSID rclsid, + __in_z LPCSTR szFriendlyClsidName, + __in DBPROPSET rgdbpsetInit[], + __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, + __out IDBCreateSession** ppidbSession + ); +HRESULT DumpErrorRecords(); +static HRESULT FileSpecToString( + __in const SQL_FILESPEC* psf, + __out LPWSTR* ppwz + ); +static HRESULT EscapeSqlIdentifier( + __in_z LPCWSTR wzDatabase, + __deref_out_z LPWSTR* ppwz + ); + + +/******************************************************************** + SqlConnectDatabase - establishes a connection to a database + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlConnectDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out IDBCreateSession** ppidbSession + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession); + + HRESULT hr = S_OK; + LPWSTR pwzServerInstance = NULL; + DBPROP rgdbpInit[4] = { }; + DBPROPSET rgdbpsetInit[1] = { }; + ULONG cProperties = 0; + + // if there is an instance + if (wzInstance && *wzInstance) + { + hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance); + } + else + { + hr = StrAllocString(&pwzServerInstance, wzServer, 0); + } + SqlExitOnFailure(hr, "failed to allocate memory for the server instance"); + + // server[\instance] + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance); + ++cProperties; + + // database + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase); + ++cProperties; + + if (fIntegratedAuth) + { + // username + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication + ++cProperties; + } + else + { + // username + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser); + ++cProperties; + + // password + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword); + ++cProperties; + } + + // put the properties into a set + rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpsetInit[0].rgProperties = rgdbpInit; + rgdbpsetInit[0].cProperties = cProperties; + + // obtain access to the SQL Native Client provider + hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); + if (FAILED(hr)) + { + SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB..."); + + // try OLE DB but if that fails return original error failure + HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); + if (FAILED(hr2)) + { + SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up."); + } + else + { + hr = S_OK; + } + } + +LExit: + for (; 0 < cProperties; cProperties--) + { + ::VariantClear(&rgdbpInit[cProperties - 1].vValue); + } + + ReleaseStr(pwzServerInstance); + + return hr; +} + + +/******************************************************************** + SqlStartTransaction - Starts a new transaction that must be ended + +*********************************************************************/ +extern "C" HRESULT DAPI SqlStartTransaction( + __in IDBCreateSession* pidbSession, + __out IDBCreateCommand** ppidbCommand, + __out ITransaction** ppit + ) +{ + Assert(pidbSession && ppit); + + HRESULT hr = S_OK; + + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand); + SqlExitOnFailure(hr, "unable to create command from session"); + + hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit); + SqlExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal"); + + hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); + +LExit: + + return hr; +} + +/******************************************************************** + SqlEndTransaction - Ends the transaction + + NOTE: if fCommit, will commit the transaction, otherwise rolls back +*********************************************************************/ +extern "C" HRESULT DAPI SqlEndTransaction( + __in ITransaction* pit, + __in BOOL fCommit + ) +{ + Assert(pit); + + HRESULT hr = S_OK; + + if (fCommit) + { + hr = pit->Commit(FALSE, XACTTC_SYNC, 0); + SqlExitOnFailure(hr, "commit of transaction failed"); + } + else + { + hr = pit->Abort(NULL, FALSE, FALSE); + SqlExitOnFailure(hr, "abort of transaction failed"); + } + +LExit: + + return hr; +} + + +/******************************************************************** + SqlDatabaseExists - determines if database exists + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored + returns S_OK if database exist + returns S_FALSE if database does not exist + returns E_* on error +********************************************************************/ +extern "C" HRESULT DAPI SqlDatabaseExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDatabaseExists - determines if database exists + + NOTE: pidbSession must be connected to master database + returns S_OK if database exist + returns S_FALSE if database does not exist + returns E_* on error +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDatabaseExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + + LPWSTR pwzQuery = NULL; + IRowset* pirs = NULL; + + DBCOUNTITEM cRows = 0; + HROW rghRows[1]; + HROW* prow = rghRows; + + // + // query to see if the database exists + // + hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase); + SqlExitOnFailure(hr, "failed to allocate query string to ensure database exists"); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to get database list from 'master' database"); + Assert(pirs); + + // + // check to see if the database was returned + // + hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow); + SqlExitOnFailure(hr, "failed to get row with database name"); + + // succeeded but no database + if ((DB_S_ENDOFROWSET == hr) || (0 == cRows)) + { + hr = S_FALSE; + } + +LExit: + ReleaseObject(pirs); + ReleaseStr(pwzQuery); + + return hr; +} + + +/******************************************************************** + SqlDatabaseEnsureExists - creates a database if it does not exist + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlDatabaseEnsureExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to create the new database + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + + Assert(S_OK == hr); +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDatabaseEnsureExists - creates a database if it does not exist + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + + if (S_FALSE == hr) + { + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + } + // else database already exists, return S_FALSE + + Assert(S_OK == hr); +LExit: + + return hr; +} + + +/******************************************************************** + SqlCreateDatabase - creates a database on the server + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlCreateDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to create the new database + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + + Assert(S_OK == hr); +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionCreateDatabase - creates a database on the server + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionCreateDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzDbFile = NULL; + LPWSTR pwzLogFile = NULL; + LPWSTR pwzQuery = NULL; + LPWSTR pwzDatabaseEscaped = NULL; + + if (psfDatabase) + { + hr = FileSpecToString(psfDatabase, &pwzDbFile); + SqlExitOnFailure(hr, "failed to convert db filespec to string"); + } + + if (psfLog) + { + hr = FileSpecToString(psfLog, &pwzLogFile); + SqlExitOnFailure(hr, "failed to convert log filespec to string"); + } + + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to escape database string"); + + 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""); + SqlExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery); + +LExit: + ReleaseStr(pwzQuery); + ReleaseStr(pwzLogFile); + ReleaseStr(pwzDbFile); + ReleaseStr(pwzDatabaseEscaped); + + return hr; +} + + +/******************************************************************** + SqlDropDatabase - removes a database from a server if it exists + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlDropDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to search for wzDatabase + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "Failed to connect to 'master' database"); + + hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription); + +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDropDatabase - removes a database from a server if it exists + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDropDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + LPWSTR pwzQuery = NULL; + LPWSTR pwzDatabaseEscaped = NULL; + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to escape database string"); + + if (S_OK == hr) + { + hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); + SqlExitOnFailure(hr, "Failed to drop database"); + } + +LExit: + ReleaseStr(pwzQuery); + ReleaseStr(pwzDatabaseEscaped); + + return hr; +} + + +/******************************************************************** + SqlSessionExecuteQuery - executes a query and returns the results if desired + + NOTE: ppirs and pcRoes and pbstrErrorDescription are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionExecuteQuery( + __in IDBCreateSession* pidbSession, + __in __sql_command LPCWSTR wzSql, + __out_opt IRowset** ppirs, + __out_opt DBROWCOUNT* pcRows, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession); + + HRESULT hr = S_OK; + IDBCreateCommand* pidbCommand = NULL; + ICommandText* picmdText = NULL; + ICommand* picmd = NULL; + DBROWCOUNT cRows = 0; + + if (pcRows) + { + *pcRows = NULL; + } + + // + // create the command + // + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand); + SqlExitOnFailure(hr, "failed to create database session"); + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); + SqlExitOnFailure(hr, "failed to create command to execute session"); + + // + // set the sql text into the command + // + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); + SqlExitOnFailure(hr, "failed to get command text object for command"); + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); + SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + + // + // execute the command + // + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); + SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + + if (DB_S_ERRORSOCCURRED == hr) + { + hr = E_FAIL; + } + + if (pcRows) + { + *pcRows = cRows; + } + +LExit: + + if (FAILED(hr) && picmd && pbstrErrorDescription) + { + HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English + if (FAILED(hrGetErrors)) + { + ReleaseBSTR(*pbstrErrorDescription); + } + } + + ReleaseObject(picmd); + ReleaseObject(picmdText); + ReleaseObject(pidbCommand); + + return hr; +} + + +/******************************************************************** + SqlCommandExecuteQuery - executes a SQL command and returns the results if desired + + NOTE: ppirs and pcRoes are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlCommandExecuteQuery( + __in IDBCreateCommand* pidbCommand, + __in __sql_command LPCWSTR wzSql, + __out IRowset** ppirs, + __out DBROWCOUNT* pcRows + ) +{ + Assert(pidbCommand); + + HRESULT hr = S_OK; + ICommandText* picmdText = NULL; + ICommand* picmd = NULL; + DBROWCOUNT cRows = 0; + + if (pcRows) + { + *pcRows = NULL; + } + + // + // create the command + // + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); + SqlExitOnFailure(hr, "failed to create command to execute session"); + + // + // set the sql text into the command + // + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); + SqlExitOnFailure(hr, "failed to get command text object for command"); + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); + SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + + // + // execute the command + // + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); + SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + + if (DB_S_ERRORSOCCURRED == hr) + { + hr = E_FAIL; + } + + if (pcRows) + { + *pcRows = cRows; + } + +LExit: + ReleaseObject(picmd); + ReleaseObject(picmdText); + + return hr; +} + + +/******************************************************************** + SqlGetErrorInfo - gets error information from the last SQL function call + + NOTE: pbstrErrorSource and pbstrErrorDescription are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlGetErrorInfo( + __in IUnknown* pObjectWithError, + __in REFIID IID_InterfaceWithError, + __in DWORD dwLocaleId, + __out_opt BSTR* pbstrErrorSource, + __out_opt BSTR* pbstrErrorDescription + ) +{ + HRESULT hr = S_OK; + Assert(pObjectWithError); + + // interfaces needed to extract error information out + ISupportErrorInfo* pISupportErrorInfo = NULL; + IErrorInfo* pIErrorInfoAll = NULL; + IErrorRecords* pIErrorRecords = NULL; + IErrorInfo* pIErrorInfoRecord = NULL; + + // only ask for error information if the interface supports it. + hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo); + SqlExitOnFailure(hr, "No error information was found for object."); + + hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError); + SqlExitOnFailure(hr, "InterfaceWithError is not supported for object with error"); + + // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway + hr = ::GetErrorInfo(0, &pIErrorInfoAll); + SqlExitOnFailure(hr, "failed to get error info"); + + if (S_OK == hr && pIErrorInfoAll) + { + // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records + hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); + if (SUCCEEDED(hr)) + { + ULONG cErrors = 0; + pIErrorRecords->GetRecordCount(&cErrors); + + // get the error information for each record + for (ULONG i = 0; i < cErrors; ++i) + { + hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord); + if (SUCCEEDED(hr)) + { + if (pbstrErrorSource) + { + pIErrorInfoRecord->GetSource(pbstrErrorSource); + } + if (pbstrErrorDescription) + { + pIErrorInfoRecord->GetDescription(pbstrErrorDescription); + } + + ReleaseNullObject(pIErrorInfoRecord); + + break; // TODO: return more than one error in the future! + } + } + + ReleaseNullObject(pIErrorRecords); + } + else // we have a simple error record + { + if (pbstrErrorSource) + { + pIErrorInfoAll->GetSource(pbstrErrorSource); + } + if (pbstrErrorDescription) + { + pIErrorInfoAll->GetDescription(pbstrErrorDescription); + } + } + } + else + { + hr = E_NOMOREITEMS; + } + +LExit: + ReleaseObject(pIErrorInfoRecord); + ReleaseObject(pIErrorRecords); + ReleaseObject(pIErrorInfoAll); + ReleaseObject(pISupportErrorInfo); + + return hr; +} + + +// +// private +// + +static HRESULT InitializeDatabaseConnection( + __in REFCLSID rclsid, + __in_z LPCSTR szFriendlyClsidName, + __in DBPROPSET rgdbpsetInit[], + __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, + __out IDBCreateSession** ppidbSession +) +{ + Unused(szFriendlyClsidName); // only used in DEBUG builds + + HRESULT hr = S_OK; + IDBInitialize* pidbInitialize = NULL; + IDBProperties* pidbProperties = NULL; + + hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize); + SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName); + + // create and set the property set + hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); + SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName); + + hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit); + SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName); + + // initialize connection to datasource + hr = pidbInitialize->Initialize(); + if (FAILED(hr)) + { + DumpErrorRecords(); + } + SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName); + + hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); + SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName); + +LExit: + ReleaseObject(pidbProperties); + ReleaseObject(pidbInitialize); + + return hr; +} + +HRESULT DumpErrorRecords() +{ + HRESULT hr = S_OK; + IErrorInfo* pIErrorInfo = NULL; + IErrorRecords* pIErrorRecords = NULL; + IErrorInfo* pIErrorInfoRecord = NULL; + BSTR bstrDescription = NULL; + ULONG i = 0; + ULONG cRecords = 0; + ERRORINFO ErrorInfo = { }; + + // Get IErrorInfo pointer from OLE. + hr = ::GetErrorInfo(0, &pIErrorInfo); + if (FAILED(hr)) + { + ExitFunction(); + } + + // QI for IID_IErrorRecords. + hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); + if (FAILED(hr)) + { + ExitFunction(); + } + + // Get error record count. + hr = pIErrorRecords->GetRecordCount(&cRecords); + if (FAILED(hr)) + { + ExitFunction(); + } + + // Loop through the error records. + for (i = 0; i < cRecords; i++) + { + // Get pIErrorInfo from pIErrorRecords. + hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord); + + if (SUCCEEDED(hr)) + { + // Get error description and source. + hr = pIErrorInfoRecord->GetDescription(&bstrDescription); + + // Retrieve the ErrorInfo structures. + hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo); + + SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription); + + ReleaseNullObject(pIErrorInfoRecord); + ReleaseNullBSTR(bstrDescription); + } + } + +LExit: + ReleaseNullBSTR(bstrDescription); + ReleaseObject(pIErrorInfoRecord); + ReleaseObject(pIErrorRecords); + ReleaseObject(pIErrorInfo); + + return hr; +} + +/******************************************************************** + FileSpecToString + +*********************************************************************/ +static HRESULT FileSpecToString( + __in const SQL_FILESPEC* psf, + __out LPWSTR* ppwz + ) +{ + Assert(psf && ppwz); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + hr = StrAllocString(&pwz, L"(", 1024); + SqlExitOnFailure(hr, "failed to allocate string for database file info"); + + SqlExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info"); + SqlExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info"); + + hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName); + SqlExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName); + + hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename); + SqlExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename); + + if (0 != psf->wzSize[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize); + SqlExitOnFailure(hr, "failed to format database file info size: %ls", psf->wzSize); + } + + if (0 != psf->wzMaxSize[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize); + SqlExitOnFailure(hr, "failed to format database file info maxsize: %ls", psf->wzMaxSize); + } + + if (0 != psf->wzGrow[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow); + SqlExitOnFailure(hr, "failed to format database file info growth: %ls", psf->wzGrow); + } + + hr = StrAllocFormatted(&pwz, L"%s)", pwz); + SqlExitOnFailure(hr, "failed to allocate string for file spec"); + + *ppwz = pwz; + pwz = NULL; // null here so it doesn't get freed below + +LExit: + ReleaseStr(pwz); + return hr; +} + +static HRESULT EscapeSqlIdentifier( + __in_z LPCWSTR wzIdentifier, + __deref_out_z LPWSTR* ppwz + ) +{ + Assert(ppwz); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + if (wzIdentifier == NULL) + { + //Just ignore a NULL identifier and clear out the result + ReleaseNullStr(*ppwz); + ExitFunction(); + } + + int cchIdentifier = lstrlenW(wzIdentifier); + + //If an empty string or already escaped just copy + if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']')) + { + hr = StrAllocString(&pwz, wzIdentifier, 0); + SqlExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier); + } + else + { + //escape it + hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier); + SqlExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier); + } + + *ppwz = pwz; + pwz = NULL; // null here so it doesn't get freed below + +LExit: + ReleaseStr(pwz); + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define SrpExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) +#define SrpExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) +#define SrpExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) +#define SrpExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) +#define SrpExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SRPUTIL, e, x, s, __VA_ARGS__) +#define SrpExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SRPUTIL, g, x, s, __VA_ARGS__) + + +typedef BOOL (WINAPI *PFN_SETRESTOREPTW)( + __in PRESTOREPOINTINFOW pRestorePtSpec, + __out PSTATEMGRSTATUS pSMgrStatus + ); + +static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL; +static HMODULE vhSrClientDll = NULL; + + +static HRESULT InitializeComSecurity(); + + +DAPI_(HRESULT) SrpInitialize( + __in BOOL fInitializeComSecurity + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"srclient.dll", &vhSrClientDll); + if (FAILED(hr)) + { + ExitFunction1(hr = E_NOTIMPL); + } + + vpfnSRSetRestorePointW = reinterpret_cast(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW")); + SrpExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address."); + + // If allowed, initialize COM security to enable NetworkService, + // LocalService and System to make callbacks to the process + // calling System Restore. This is required for any process + // that calls SRSetRestorePoint. + if (fInitializeComSecurity) + { + hr = InitializeComSecurity(); + SrpExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore."); + } + +LExit: + if (FAILED(hr) && vhSrClientDll) + { + SrpUninitialize(); + } + + return hr; +} + +DAPI_(void) SrpUninitialize() +{ + if (vhSrClientDll) + { + ::FreeLibrary(vhSrClientDll); + vhSrClientDll = NULL; + vpfnSRSetRestorePointW = NULL; + } +} + +DAPI_(HRESULT) SrpCreateRestorePoint( + __in_z LPCWSTR wzApplicationName, + __in SRP_ACTION action + ) +{ + HRESULT hr = S_OK; + RESTOREPOINTINFOW restorePoint = { }; + STATEMGRSTATUS status = { }; + + if (!vpfnSRSetRestorePointW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + restorePoint.dwEventType = BEGIN_SYSTEM_CHANGE; + restorePoint.dwRestorePtType = (SRP_ACTION_INSTALL == action) ? APPLICATION_INSTALL : (SRP_ACTION_UNINSTALL == action) ? APPLICATION_UNINSTALL : MODIFY_SETTINGS; + ::StringCbCopyW(restorePoint.szDescription, sizeof(restorePoint.szDescription), wzApplicationName); + + if (!vpfnSRSetRestorePointW(&restorePoint, &status)) + { + SrpExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point."); + } + +LExit: + return hr; +} + + +// internal functions. + +static HRESULT InitializeComSecurity() +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + SECURITY_DESCRIPTOR sd = {0}; + EXPLICIT_ACCESS ea[5] = {0}; + ACL* pAcl = NULL; + ULONGLONG rgSidBA[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidLS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidNS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidPS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidSY[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + DWORD cbSid = 0; + + // Create the security descriptor explicitly as follows because + // CoInitializeSecurity() will not accept the relative security descriptors + // returned by ConvertStringSecurityDescriptorToSecurityDescriptor(). + // + // The result is a security descriptor that is equivalent to the following + // security descriptor definition language (SDDL) string: + // + // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)(A;;0x1;;;SY)(A;;0x1;;;BA) + // + + // Initialize the security descriptor. + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) + { + SrpExitWithLastError(hr, "Failed to initialize security descriptor for system restore."); + } + + // Create an administrator group security identifier (SID). + cbSid = sizeof(rgSidBA); + if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create administrator SID for system restore."); + } + + // Create a local service security identifier (SID). + cbSid = sizeof(rgSidLS); + if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create local service SID for system restore."); + } + + // Create a network service security identifier (SID). + cbSid = sizeof(rgSidNS); + if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create network service SID for system restore."); + } + + // Create a personal account security identifier (SID). + cbSid = sizeof(rgSidPS); + if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create self SID for system restore."); + } + + // Create a local service security identifier (SID). + cbSid = sizeof(rgSidSY); + if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create local system SID for system restore."); + } + + // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and + // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required. + ea[0].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[0].grfAccessMode = SET_ACCESS; + ea[0].grfInheritance = NO_INHERITANCE; + ea[0].Trustee.pMultipleTrustee = NULL; + ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[0].Trustee.ptstrName = (LPTSTR)rgSidBA; + + ea[1].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[1].grfAccessMode = SET_ACCESS; + ea[1].grfInheritance = NO_INHERITANCE; + ea[1].Trustee.pMultipleTrustee = NULL; + ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[1].Trustee.ptstrName = (LPTSTR)rgSidLS; + + ea[2].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[2].grfAccessMode = SET_ACCESS; + ea[2].grfInheritance = NO_INHERITANCE; + ea[2].Trustee.pMultipleTrustee = NULL; + ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[2].Trustee.ptstrName = (LPTSTR)rgSidNS; + + ea[3].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[3].grfAccessMode = SET_ACCESS; + ea[3].grfInheritance = NO_INHERITANCE; + ea[3].Trustee.pMultipleTrustee = NULL; + ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[3].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[3].Trustee.ptstrName = (LPTSTR)rgSidPS; + + ea[4].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[4].grfAccessMode = SET_ACCESS; + ea[4].grfInheritance = NO_INHERITANCE; + ea[4].Trustee.pMultipleTrustee = NULL; + ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[4].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[4].Trustee.ptstrName = (LPTSTR)rgSidSY; + + // Create an access control list (ACL) using this ACE list. + er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl); + SrpExitOnWin32Error(er, hr, "Failed to create ACL for system restore."); + + // Set the security descriptor owner to Administrators. + if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE)) + { + SrpExitWithLastError(hr, "Failed to set administrators owner for system restore."); + } + + // Set the security descriptor group to Administrators. + if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE)) + { + SrpExitWithLastError(hr, "Failed to set administrators group access for system restore."); + } + + // Set the discretionary access control list (DACL) to the ACL. + if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) + { + SrpExitWithLastError(hr, "Failed to set DACL for system restore."); + } + + // Note that an explicit security descriptor is being passed in. + 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); + SrpExitOnFailure(hr, "Failed to initialize COM security for system restore."); + +LExit: + if (pAcl) + { + ::LocalFree(pAcl); + } + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define StrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) +#define StrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) +#define StrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) +#define StrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) +#define StrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_STRUTIL, e, x, s, __VA_ARGS__) +#define StrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_STRUTIL, g, x, s, __VA_ARGS__) + +#define ARRAY_GROWTH_SIZE 5 + +// Forward declarations. +static HRESULT AllocHelper( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocStringHelper( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocConcatHelper( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocFormattedArgsHelper( + __deref_out_z LPWSTR* ppwz, + __in BOOL fZeroOnRealloc, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +static HRESULT StrAllocStringMapInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in DWORD dwMapFlags + ); + +/******************************************************************** +StrAlloc - allocates or reuses dynamic string memory + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAlloc( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ) +{ + return AllocHelper(ppwz, cch, FALSE); +} + +/******************************************************************** +StrAllocSecure - allocates or reuses dynamic string memory +If the memory needs to reallocated, calls SecureZeroMemory on the +original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocSecure( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ) +{ + return AllocHelper(ppwz, cch, TRUE); +} + +/******************************************************************** +AllocHelper - allocates or reuses dynamic string memory +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +static HRESULT AllocHelper( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && cch); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppwz) + { + if (fZeroOnRealloc) + { + LPVOID pvNew = NULL; + hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew); + StrExitOnFailure(hr, "Failed to reallocate string"); + pwz = static_cast(pvNew); + } + else + { + pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE)); + } + } + else + { + pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); + } + + StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppwz = pwz; +LExit: + return hr; +} + + +/******************************************************************** +StrTrimCapacity - Frees any unnecessary memory associated with a string. + Purely used for optimization, generally only when a string + has been changing size, and will no longer grow. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrTrimCapacity( + __deref_out_z LPWSTR* ppwz + ) +{ + Assert(ppwz); + + HRESULT hr = S_OK; + SIZE_T cchLen = 0; + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + StrExitOnRootFailure(hr, "Failed to calculate length of string"); + + ++cchLen; // Add 1 for null-terminator + + hr = StrAlloc(ppwz, cchLen); + StrExitOnFailure(hr, "Failed to reallocate string"); + +LExit: + return hr; +} + + +/******************************************************************** +StrTrimWhitespace - allocates or reuses dynamic string memory and copies + in an existing string, excluding whitespace + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrTrimWhitespace( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource + ) +{ + HRESULT hr = S_OK; + size_t i = 0; + LPWSTR sczResult = NULL; + + // Ignore beginning whitespace + while (L' ' == *wzSource || L'\t' == *wzSource) + { + wzSource++; + } + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &i); + StrExitOnRootFailure(hr, "Failed to get length of string"); + + // Overwrite ending whitespace with null characters + if (0 < i) + { + // start from the last non-null-terminator character in the array + for (i = i - 1; i > 0; --i) + { + if (L' ' != wzSource[i] && L'\t' != wzSource[i]) + { + break; + } + } + + ++i; + } + + hr = StrAllocString(&sczResult, wzSource, i); + StrExitOnFailure(hr, "Failed to copy result string"); + + // Output result + *ppwz = sczResult; + sczResult = NULL; + +LExit: + ReleaseStr(sczResult); + + return hr; +} + + +/******************************************************************** +StrAnsiAlloc - allocates or reuses dynamic ANSI string memory + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAlloc( + __deref_out_ecount_part(cch, 0) LPSTR* ppsz, + __in SIZE_T cch + ) +{ + Assert(ppsz && cch); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppsz) + { + psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE)); + } + else + { + psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); + } + + StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppsz = psz; +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string. + Purely used for optimization, generally only when a string + has been changing size, and will no longer grow. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrAnsiTrimCapacity( + __deref_out_z LPSTR* ppz + ) +{ + Assert(ppz); + + HRESULT hr = S_OK; + SIZE_T cchLen = 0; + +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); +#pragma prefast(pop) + StrExitOnFailure(hr, "Failed to calculate length of string"); + + ++cchLen; // Add 1 for null-terminator + + hr = StrAnsiAlloc(ppz, cchLen); + StrExitOnFailure(hr, "Failed to reallocate string"); + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies + in an existing string, excluding whitespace + +NOTE: caller is responsible for freeing ppz even if function fails +********************************************************************/ +HRESULT DAPI StrAnsiTrimWhitespace( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR szSource + ) +{ + HRESULT hr = S_OK; + size_t i = 0; + LPSTR sczResult = NULL; + + // Ignore beginning whitespace + while (' ' == *szSource || '\t' == *szSource) + { + szSource++; + } + + hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, &i); + StrExitOnRootFailure(hr, "Failed to get length of string"); + + // Overwrite ending whitespace with null characters + if (0 < i) + { + // start from the last non-null-terminator character in the array + for (i = i - 1; i > 0; --i) + { + if (L' ' != szSource[i] && L'\t' != szSource[i]) + { + break; + } + } + + ++i; + } + + hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i); + StrExitOnFailure(hr, "Failed to copy result string"); + + // Output result + *ppz = sczResult; + sczResult = NULL; + +LExit: + ReleaseStr(sczResult); + + return hr; +} + +/******************************************************************** +StrAllocString - allocates or reuses dynamic string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocString( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocStringHelper(ppwz, wzSource, cchSource, FALSE); +} + +/******************************************************************** +StrAllocStringSecure - allocates or reuses dynamic string memory and +copies in an existing string. If the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocStringSecure( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocStringHelper(ppwz, wzSource, cchSource, TRUE); +} + +/******************************************************************** +AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +static HRESULT AllocStringHelper( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && wzSource); // && *wzSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource && wzSource) + { + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "failed to get length of source string"); + } + + SIZE_T cchNeeded; + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator + StrExitOnRootFailure(hr, "source string is too long"); + + if (cch < cchNeeded) + { + cch = cchNeeded; + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string from string."); + } + + // copy everything (the NULL terminator will be included) + hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppsz even if function fails +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocString( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ) +{ + Assert(ppsz && wzSource); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + SIZE_T cch = 0; + SIZE_T cchDest = cchSource; // at least enough + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL); + if (0 == cchDest) + { + StrExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource); + } + + --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below + } + 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 + { + cchDest = cchSource - 1; + } + + if (cch < cchDest + 1) + { + cch = cchDest + 1; // add one for the NULL terminator + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppsz) + { + psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE)); + } + else + { + psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); + } + StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppsz = psz; + } + + if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL)) + { + StrExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource); + } + (*ppsz)[cchDest] = L'\0'; + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ) +{ + Assert(ppwz && szSource); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + SIZE_T cch = 0; + SIZE_T cchDest = cchSource; // at least enough + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0); + if (0 == cchDest) + { + StrExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource); + } + + --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below + } + 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 + { + cchDest = cchSource - 1; + } + + if (cch < cchDest + 1) + { + cch = cchDest + 1; + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppwz) + { + pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE)); + } + else + { + pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); + } + + StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppwz = pwz; + } + + if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch)) + { + StrExitWithLastError(hr, "failed to convert to unicode: %s", szSource); + } + (*ppwz)[cchDest] = L'\0'; + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppsz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +HRESULT DAPI StrAnsiAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource + ) +{ + Assert(ppsz && szSource); // && *szSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnRootFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource && szSource) + { + hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "failed to get length of source string"); + } + + SIZE_T cchNeeded; + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator + StrExitOnRootFailure(hr, "source string is too long"); + + if (cch < cchNeeded) + { + cch = cchNeeded; + hr = StrAnsiAlloc(ppsz, cch); + StrExitOnFailure(hr, "failed to allocate string from string."); + } + + // copy everything (the NULL terminator will be included) +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); +#pragma prefast(pop) + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocPrefix - allocates or reuses dynamic string memory and + prefixes a string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchPrefix does not have to equal the length of wzPrefix +NOTE: if cchPrefix == 0, length of wzPrefix is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocPrefix( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzPrefix, + __in SIZE_T cchPrefix + ) +{ + Assert(ppwz && wzPrefix); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cchLen = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchPrefix) + { + hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast(&cchPrefix)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchPrefix + 1) + { + cch = cchPrefix + cchLen + 1; + hr = StrAlloc(ppwz, cch); + StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix); + } + + if (*ppwz) + { + SIZE_T cb = cch * sizeof(WCHAR); + SIZE_T cbPrefix = cchPrefix * sizeof(WCHAR); + + memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix); + memcpy(*ppwz, wzPrefix, cbPrefix); + } + else + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocConcat( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE); +} + + +/******************************************************************** +StrAllocConcatSecure - allocates or reuses dynamic string memory and +adds an existing string. If the memory needs to reallocated, calls +SecureZeroMemory on the original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocConcatSecure( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE); +} + + +/******************************************************************** +AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +static HRESULT AllocConcatHelper( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && wzSource); // && *wzSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cchLen = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchSource) + { + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchSource + 1) + { + cch = (cchSource + cchLen + 1) * 2; + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource); + } + + if (*ppwz) + { + hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + } + else + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string + +NOTE: caller is responsible for freeing ppz even if function fails +NOTE: cchSource does not have to equal the length of pzSource +NOTE: if cchSource == 0, length of pzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocConcat( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR pzSource, + __in SIZE_T cchSource + ) +{ + Assert(ppz && pzSource); // && *pzSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cchLen = 0; + + if (*ppz) + { + cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); // convert the count in bytes to count in characters + +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); +#pragma prefast(pop) + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchSource) + { +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); +#pragma prefast(pop) + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchSource + 1) + { + cch = (cchSource + cchLen + 1) * 2; + hr = StrAnsiAlloc(ppz, cch); + StrExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource); + } + + if (*ppz) + { +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); +#pragma prefast(pop) + } + else + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocFormatted - allocates or reuses dynamic string memory and formats it + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(ppwz, wzFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAllocConcatFormatted - allocates or reuses dynamic string memory +and adds a formatted string + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocConcatFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args); + va_end(args); + StrExitOnFailure(hr, "Failed to allocate formatted string"); + + hr = StrAllocConcat(ppwz, sczFormatted, 0); + +LExit: + ReleaseStr(sczFormatted); + + return hr; +} + + +/******************************************************************** +StrAllocConcatFormattedSecure - allocates or reuses dynamic string +memory and adds a formatted string. If the memory needs to be +reallocated, calls SecureZeroMemory on original block of memory after +it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args); + va_end(args); + StrExitOnFailure(hr, "Failed to allocate formatted string"); + + hr = StrAllocConcatSecure(ppwz, sczFormatted, 0); + +LExit: + ReleaseStr(sczFormatted); + + return hr; +} + + +/******************************************************************** +StrAllocFormattedSecure - allocates or reuses dynamic string memory +and formats it. If the memory needs to be reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocFormatted( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + ... + ) +{ + Assert(ppsz && szFormat && *szFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAllocFormattedArgs - allocates or reuses dynamic string memory +and formats it with the passed in args + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFormattedArgs( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args); +} + + +/******************************************************************** +StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory +and formats it with the passed in args. + +If the memory needs to reallocated, calls SecureZeroMemory on the +original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFormattedArgsSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args); +} + + +/******************************************************************** +AllocFormattedArgsHelper - allocates or reuses dynamic string memory +and formats it with the passed in args. + +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +static HRESULT AllocFormattedArgsHelper( + __deref_out_z LPWSTR* ppwz, + __in BOOL fZeroOnRealloc, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + LPWSTR pwzOriginal = NULL; + SIZE_T cbOriginal = 0; + size_t cchOriginal = 0; + + if (*ppwz) + { + cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cbOriginal) + { + hr = E_INVALIDARG; + StrExitOnRootFailure(hr, "failed to get size of destination string"); + } + + cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, &cchOriginal); + StrExitOnRootFailure(hr, "failed to get length of original string"); + } + + if (0 == cch) // if there is no space in the string buffer + { + cch = 256; + + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + } + + // format the message (grow until it fits or there is a failure) + do + { + hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + if (!pwzOriginal) + { + // this allows you to pass the original string as a formatting argument and not crash + // save the original string and free it after the printf is complete + pwzOriginal = *ppwz; + *ppwz = NULL; + + // StringCchVPrintfW starts writing to the string... + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); + pwzOriginal[cchOriginal] = 0; + } + + cch *= 2; + + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + + hr = S_FALSE; + } + } while (S_FALSE == hr); + StrExitOnRootFailure(hr, "failed to format string"); + +LExit: + if (pwzOriginal && fZeroOnRealloc) + { + SecureZeroMemory(pwzOriginal, cbOriginal); + } + + ReleaseStr(pwzOriginal); + + return hr; +} + + +/******************************************************************** +StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory +and formats it with the passed in args + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + __in va_list args + ) +{ + Assert(ppsz && szFormat && *szFormat); + + HRESULT hr = S_OK; + SIZE_T cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; + LPSTR pszOriginal = NULL; + size_t cchOriginal = 0; + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnRootFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthA(*ppsz, STRSAFE_MAX_CCH, &cchOriginal); + StrExitOnRootFailure(hr, "failed to get length of original string"); + } + + if (0 == cch) // if there is no space in the string buffer + { + cch = 256; + hr = StrAnsiAlloc(ppsz, cch); + StrExitOnFailure(hr, "failed to allocate string to format: %s", szFormat); + } + + // format the message (grow until it fits or there is a failure) + do + { +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args); +#pragma prefast(pop) + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + if (!pszOriginal) + { + // this allows you to pass the original string as a formatting argument and not crash + // save the original string and free it after the printf is complete + pszOriginal = *ppsz; + *ppsz = NULL; + // StringCchVPrintfW starts writing to the string... + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); + pszOriginal[cchOriginal] = 0; + } + cch *= 2; + hr = StrAnsiAlloc(ppsz, cch); + StrExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat); + hr = S_FALSE; + } + } while (S_FALSE == hr); + StrExitOnRootFailure(hr, "failed to format string"); + +LExit: + ReleaseStr(pszOriginal); + + return hr; +} + + +/******************************************************************** +StrAllocFromError - returns the string for a particular error. + +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFromError( + __inout LPWSTR *ppwzMessage, + __in HRESULT hrError, + __in_opt HMODULE hModule, + ... + ) +{ + HRESULT hr = S_OK; + DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM; + LPVOID pvMessage = NULL; + DWORD cchMessage = 0; + + if (hModule) + { + dwFlags |= FORMAT_MESSAGE_FROM_HMODULE; + } + + va_list args; + va_start(args, hModule); + cchMessage = ::FormatMessageW(dwFlags, static_cast(hModule), hrError, 0, reinterpret_cast(&pvMessage), 0, &args); + va_end(args); + + if (0 == cchMessage) + { + StrExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError); + } + + hr = StrAllocString(ppwzMessage, reinterpret_cast(pvMessage), cchMessage); + StrExitOnFailure(hr, "Failed to allocate string for message."); + +LExit: + if (pvMessage) + { + ::LocalFree(pvMessage); + } + + return hr; +} + + +/******************************************************************** +StrMaxLength - returns maximum number of characters that can be stored in dynamic string p + +NOTE: assumes Unicode string +********************************************************************/ +extern "C" HRESULT DAPI StrMaxLength( + __in LPCVOID p, + __out SIZE_T* pcch + ) +{ + Assert(pcch); + + HRESULT hr = S_OK; + + if (p) + { + *pcch = MemSize(p); // get size of entire buffer + if (-1 == *pcch) + { + ExitFunction1(hr = E_FAIL); + } + + *pcch /= sizeof(WCHAR); // reduce to count of characters + } + else + { + *pcch = 0; + } + Assert(S_OK == hr); + +LExit: + return hr; +} + + +/******************************************************************** +StrSize - returns count of bytes in dynamic string p + +********************************************************************/ +extern "C" HRESULT DAPI StrSize( + __in LPCVOID p, + __out SIZE_T* pcb + ) +{ + Assert(p && pcb); + + HRESULT hr = S_OK; + + *pcb = MemSize(p); + if (-1 == *pcb) + { + hr = E_FAIL; + } + + return hr; +} + +/******************************************************************** +StrFree - releases dynamic string memory allocated by any StrAlloc*() functions + +********************************************************************/ +extern "C" HRESULT DAPI StrFree( + __in LPVOID p + ) +{ + Assert(p); + + HRESULT hr = MemFree(p); + StrExitOnFailure(hr, "failed to free string"); + +LExit: + return hr; +} + + +/**************************************************************************** +StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString. +Replaces all instances. + +****************************************************************************/ +extern "C" HRESULT DAPI StrReplaceStringAll( + __inout LPWSTR* ppwzOriginal, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ) +{ + HRESULT hr = S_OK; + DWORD dwStartIndex = 0; + + do + { + hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString); + StrExitOnFailure(hr, "Failed to replace substring"); + } + while (S_OK == hr); + + hr = (0 == dwStartIndex) ? S_FALSE : S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString. +Search for old substring starts at dwStartIndex. Does only 1 replace. + +****************************************************************************/ +extern "C" HRESULT DAPI StrReplaceString( + __inout LPWSTR* ppwzOriginal, + __inout DWORD* pdwStartIndex, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ) +{ + Assert(ppwzOriginal && wzOldSubString && wzNewSubString); + + HRESULT hr = S_FALSE; + LPCWSTR wzSubLocation = NULL; + LPWSTR pwzBuffer = NULL; + size_t cchOldSubString = 0; + size_t cchNewSubString = 0; + + if (!*ppwzOriginal) + { + ExitFunction(); + } + + wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString); + if (!wzSubLocation) + { + ExitFunction(); + } + + if (wzOldSubString) + { + hr = ::StringCchLengthW(wzOldSubString, STRSAFE_MAX_CCH, &cchOldSubString); + StrExitOnRootFailure(hr, "Failed to get old string length."); + } + + if (wzNewSubString) + { + hr = ::StringCchLengthW(wzNewSubString, STRSAFE_MAX_CCH, &cchNewSubString); + StrExitOnRootFailure(hr, "Failed to get new string length."); + } + + hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex); + StrExitOnRootFailure(hr, "Failed to diff pointers."); + + hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal); + StrExitOnFailure(hr, "Failed to duplicate string."); + + pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0'; + + hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0); + StrExitOnFailure(hr, "Failed to append new string."); + + hr = StrAllocConcat(&pwzBuffer, wzSubLocation + cchOldSubString, 0); + StrExitOnFailure(hr, "Failed to append post string."); + + hr = StrFree(*ppwzOriginal); + StrExitOnFailure(hr, "Failed to free original string."); + + *ppwzOriginal = pwzBuffer; + *pdwStartIndex = *pdwStartIndex + static_cast(cchNewSubString); + hr = S_OK; + +LExit: + return hr; +} + + +static inline BYTE HexCharToByte( + __in WCHAR wc + ) +{ + 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 + + BYTE b; + if (L'0' <= wc && wc <= L'9') + { + b = (BYTE)(wc - L'0'); + } + else if ('a' <= wc && wc <= 'f') + { + b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1)); + } + else // must be (L'A' <= wc && wc <= L'F') + { + b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1)); + } + + Assert(0 <= b && b <= 15); + return b; +} + + +/**************************************************************************** +StrHexEncode - converts an array of bytes to a text string + +NOTE: wzDest must have space for cbSource * 2 + 1 characters +****************************************************************************/ +extern "C" HRESULT DAPI StrHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out_ecount(cchDest) LPWSTR wzDest, + __in SIZE_T cchDest + ) +{ + Assert(pbSource && wzDest); + + HRESULT hr = S_OK; + DWORD i; + BYTE b; + + if (cchDest < 2 * cbSource + 1) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); + } + + for (i = 0; i < cbSource; ++i) + { + b = (*pbSource) >> 4; + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); + b = (*pbSource) & 0xF; + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); + + ++pbSource; + } + + *wzDest = 0; + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocHexEncode - converts an array of bytes to an allocated text string + +****************************************************************************/ +HRESULT DAPI StrAllocHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest + ) +{ + HRESULT hr = S_OK; + SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1); + + hr = StrAlloc(ppwzDest, cchSource); + StrExitOnFailure(hr, "Failed to allocate hex string."); + + hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource); + StrExitOnFailure(hr, "Failed to encode hex string."); + +LExit: + return hr; +} + + +/**************************************************************************** +StrHexDecode - converts a string of text to array of bytes + +NOTE: wzSource must contain even number of characters +****************************************************************************/ +extern "C" HRESULT DAPI StrHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(cbDest) BYTE* pbDest, + __in SIZE_T cbDest + ) +{ + Assert(wzSource && pbDest); + + HRESULT hr = S_OK; + size_t cchSource = 0; + size_t i = 0; + BYTE b = 0; + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); + StrExitOnRootFailure(hr, "Failed to get length of hex string: %ls", wzSource); + + Assert(0 == cchSource % 2); + if (cbDest < cchSource / 2) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %Iu into %Iu bytes.", wzSource, cchSource, cbDest); + } + + for (i = 0; i < cchSource / 2; ++i) + { + b = HexCharToByte(*wzSource++); + (*pbDest) = b << 4; + + b = HexCharToByte(*wzSource++); + (*pbDest) |= b & 0xF; + + ++pbDest; + } + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocHexDecode - allocates a byte array hex-converted from string of text + +NOTE: wzSource must contain even number of characters +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(*pcbDest) BYTE** ppbDest, + __out_opt DWORD* pcbDest + ) +{ + Assert(wzSource && *wzSource && ppbDest); + + HRESULT hr = S_OK; + size_t cch = 0; + BYTE* pb = NULL; + DWORD cb = 0; + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch); + StrExitOnFailure(hr, "Failed to calculate length of source string."); + + if (cch % 2) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded."); + } + + cb = static_cast(cch / 2); + pb = static_cast(MemAlloc(cb, TRUE)); + StrExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode."); + + hr = StrHexDecode(wzSource, pb, cb); + StrExitOnFailure(hr, "Failed to decode source string."); + + *ppbDest = pb; + pb = NULL; + + if (pcbDest) + { + *pcbDest = cb; + } + +LExit: + ReleaseMem(pb); + + return hr; +} + + +/**************************************************************************** +Base85 encoding/decoding data + +****************************************************************************/ +const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"; + +const BYTE Base85DecodeTable[256] = +{ + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54, + 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85 +}; + +const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 }; + + +/**************************************************************************** +StrAllocBase85Encode - converts an array of bytes into an XML compatible string + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocBase85Encode( + __in_bcount_opt(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_z LPWSTR* pwzDest + ) +{ + HRESULT hr = S_OK; + SIZE_T cchDest = 0; + LPWSTR wzDest; + DWORD_PTR iSource = 0; + DWORD_PTR iDest = 0; + + if (!pwzDest || !pbSource) + { + return E_INVALIDARG; + } + + // calc actual size of output + cchDest = cbSource / 4; + cchDest += cchDest * 4; + if (cbSource & 3) + { + cchDest += (cbSource & 3) + 1; + } + ++cchDest; // add room for null terminator + + hr = StrAlloc(pwzDest, cchDest); + StrExitOnFailure(hr, "failed to allocate destination string"); + + wzDest = *pwzDest; + + // first, encode full words + for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5) + { + DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24); + DWORD k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest] = Base85EncodeTable[n - k * 85]; + n = k / 85; + + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 1] = Base85EncodeTable[k - n * 85]; + k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 2] = Base85EncodeTable[n - k * 85]; + n = k / 85; + + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 3] = Base85EncodeTable[k - n * 85]; + + __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85); + + //Assert(0 <= n && n < countof(Base85EncodeTable)); + wzDest[iDest + 4] = Base85EncodeTable[n]; + } + + // encode any remaining bytes + if (iSource < cbSource) + { + DWORD n = 0; + for (DWORD i = 0; iSource + i < cbSource; ++i) + { + n += pbSource[iSource + i] << (i << 3); + } + + for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest) + { + DWORD k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest] = Base85EncodeTable[n - k * 85]; + + n = k; + } + + wzDest[iDest] = Base85EncodeTable[n]; + ++iDest; + } + Assert(iSource == cbSource); + Assert(iDest == cchDest - 1); + + wzDest[iDest] = L'\0'; + hr = S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocBase85Decode - converts a string of text to array of bytes + +NOTE: Use MemFree() to release the allocated stream of bytes +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocBase85Decode( + __in_z LPCWSTR wzSource, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out SIZE_T* pcbDest + ) +{ + HRESULT hr = S_OK; + size_t cchSource = 0; + DWORD_PTR i, n, k; + + BYTE* pbDest = 0; + SIZE_T cbDest = 0; + + if (!wzSource || !ppbDest || !pcbDest) + { + ExitFunction1(hr = E_INVALIDARG); + } + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); + StrExitOnRootFailure(hr, "failed to get length of base 85 string: %ls", wzSource); + + // evaluate size of output and check it + k = cchSource / 5; + cbDest = k << 2; + k = cchSource - k * 5; + if (k) + { + if (1 == k) + { + // decode error -- encoded size cannot equal 1 mod 5 + return E_UNEXPECTED; + } + + cbDest += k - 1; + } + + *ppbDest = static_cast(MemAlloc(cbDest, FALSE)); + StrExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string"); + + pbDest = *ppbDest; + *pcbDest = cbDest; + + // decode full words first + while (5 <= cchSource) + { + k = Base85DecodeTable[wzSource[0]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n = k; + + k = Base85DecodeTable[wzSource[1]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * 85; + + k = Base85DecodeTable[wzSource[2]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * (85 * 85); + + k = Base85DecodeTable[wzSource[3]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * (85 * 85 * 85); + + k = Base85DecodeTable[wzSource[4]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + k *= (85 * 85 * 85 * 85); + + // if (k + n > (1u << 32)) <=> (k > ~n) then decode error + if (k > ~n) + { + // overflow + return E_UNEXPECTED; + } + + n += k; + + pbDest[0] = (BYTE) n; + pbDest[1] = (BYTE) (n >> 8); + pbDest[2] = (BYTE) (n >> 16); + pbDest[3] = (BYTE) (n >> 24); + + wzSource += 5; + pbDest += 4; + cchSource -= 5; + } + + if (cchSource) + { + n = 0; + for (i = 0; i < cchSource; ++i) + { + k = Base85DecodeTable[wzSource[i]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + + n += k * Base85PowerTable[i]; + } + + for (i = 1; i < cchSource; ++i) + { + *pbDest++ = (BYTE)n; + n >>= 8; + } + + if (0 != n) + { + // decode error + return E_UNEXPECTED; + } + } + + hr = S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +MultiSzLen - calculates the length of a MULTISZ string including all nulls +including the double null terminator at the end of the MULTISZ. + +NOTE: returns 0 if the multisz in not properly terminated with two nulls +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzLen( + __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, + __out SIZE_T* pcch +) +{ + Assert(pcch); + + HRESULT hr = S_OK; + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwMaxSize = 0; + + hr = StrMaxLength(pwzMultiSz, &dwMaxSize); + StrExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length"); + + *pcch = 0; + while (*pcch < dwMaxSize) + { + if (L'\0' == *wz && L'\0' == *(wz + 1)) + { + break; + } + + ++wz; + *pcch = *pcch + 1; + } + + // Add two for the last 2 NULLs (that we looked ahead at) + *pcch = *pcch + 2; + + // If we've walked off the end then the length is 0 + if (*pcch > dwMaxSize) + { + *pcch = 0; + } + +LExit: + return hr; +} + + +/**************************************************************************** +MultiSzPrepend - prepends a string onto the front of a MUTLISZ + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzPrepend( + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in __nullnullterminated LPCWSTR pwzInsert + ) +{ + Assert(ppwzMultiSz && pwzInsert && *pwzInsert); + + HRESULT hr =S_OK; + LPWSTR pwzResult = NULL; + SIZE_T cchResult = 0; + SIZE_T cchInsert = 0; + SIZE_T cchMultiSz = 0; + + // Get the lengths of the MULTISZ (and prime it if it's not initialized) + if (pcchMultiSz && 0 != *pcchMultiSz) + { + cchMultiSz = *pcchMultiSz; + } + else + { + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get length of multisz"); + } + + hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchInsert)); + StrExitOnRootFailure(hr, "failed to get length of insert string"); + + cchResult = cchInsert + cchMultiSz + 1; + + // Allocate the result buffer + hr = StrAlloc(&pwzResult, cchResult + 1); + StrExitOnFailure(hr, "failed to allocate result string"); + + // Prepend + hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert); + StrExitOnRootFailure(hr, "failed to copy prepend string: %ls", pwzInsert); + + // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in + if (0 == cchMultiSz) + { + pwzResult[cchResult] = L'\0'; + ++cchResult; + } + else + { + // Copy the rest + ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR)); + + // Free the old buffer + ReleaseNullStr(*ppwzMultiSz); + } + + // Set the result + *ppwzMultiSz = pwzResult; + + if (pcchMultiSz) + { + *pcchMultiSz = cchResult; + } + + pwzResult = NULL; + +LExit: + ReleaseNullStr(pwzResult); + + return hr; +} + +/**************************************************************************** +MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the +specified sub string and returns the index of the +string in the MULTISZ, the address, neither, or both + +NOTE: returns S_FALSE if the string is not found +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzFindSubstring( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzSubstring, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn + ) +{ + Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring); + + HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty) + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwIndex = 0; + SIZE_T cchMultiSz = 0; + SIZE_T cchProgress = 0; + + hr = MultiSzLen(pwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the string containing the sub string + hr = S_OK; + while (NULL == wcsistr(wz, pwzSubstring)) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++dwIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found it give them what they want + if (S_OK == hr) + { + if (pdwIndex) + { + *pdwIndex = dwIndex; + } + + if (ppwzFoundIn) + { + *ppwzFoundIn = wz; + } + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzFindString - finds a string in a MULTISZ and returns the index of +the string in the MULTISZ, the address or both + +NOTE: returns S_FALSE if the string is not found +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzFindString( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzString, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound + ) +{ + Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound)); + + HRESULT hr = S_FALSE; // Assume we won't find it + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwIndex = 0; + SIZE_T cchMutliSz = 0; + SIZE_T cchProgress = 0; + + hr = MultiSzLen(pwzMultiSz, &cchMutliSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the string + hr = S_OK; + while (0 != lstrcmpW(wz, pwzString)) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMutliSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++dwIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found it give them what they want + if (S_OK == hr) + { + if (pdwIndex) + { + *pdwIndex = dwIndex; + } + + if (ppwzFound) + { + *ppwzFound = wz; + } + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzRemoveString - removes string from a MULTISZ at the specified +index + +NOTE: does an in place removal without shrinking the memory allocation + +NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzRemoveString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex + ) +{ + Assert(ppwzMultiSz && *ppwzMultiSz); + + HRESULT hr = S_OK; + LPCWSTR wz = *ppwzMultiSz; + LPCWSTR wzNext = NULL; + DWORD_PTR dwCurrentIndex = 0; + SIZE_T cchMultiSz = 0; + SIZE_T cchProgress = 0; + + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the index we want to remove + hr = S_OK; + while (dwCurrentIndex < dwIndex) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++cchProgress; + ++dwCurrentIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found the index to be removed + if (S_OK == hr) + { + wzNext = wz; + + // Slide through to the end of the current string + while (L'\0' != *wzNext && cchProgress < cchMultiSz) + { + ++wzNext; + ++cchProgress; + } + + // Something weird has happened if we're past the end of the MULTISZ + if (cchProgress > cchMultiSz) + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ"); + } + + // Move on to the next character + ++wzNext; + ++cchProgress; + + ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR)); + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzInsertString - inserts new string at the specified index + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzInsertString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzInsert + ) +{ + Assert(ppwzMultiSz && pwzInsert && *pwzInsert); + + HRESULT hr = S_OK; + LPCWSTR wz = *ppwzMultiSz; + DWORD_PTR dwCurrentIndex = 0; + SIZE_T cchProgress = 0; + LPWSTR pwzResult = NULL; + SIZE_T cchResult = 0; + SIZE_T cchString = 0; + SIZE_T cchMultiSz = 0; + + hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchString)); + StrExitOnRootFailure(hr, "failed to get length of insert string"); + + if (pcchMultiSz && 0 != *pcchMultiSz) + { + cchMultiSz = *pcchMultiSz; + } + else + { + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + } + + // Find the index we want to insert at + hr = S_OK; + while (dwCurrentIndex < dwIndex) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz) + { + hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND); + StrExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex); + } + + // Move on to the next string + ++wz; + ++cchProgress; + ++dwCurrentIndex; + } + + // + // Insert the string + // + cchResult = cchMultiSz + cchString + 1; + + hr = StrAlloc(&pwzResult, cchResult); + StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); + + // Copy the part before the insert + ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR)); + + // Copy the insert part + ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR)); + + // Copy the part after the insert + ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR)); + + // Free the old buffer + ReleaseNullStr(*ppwzMultiSz); + + // Set the result + *ppwzMultiSz = pwzResult; + + // If they wanted the resulting length, let 'em have it + if (pcchMultiSz) + { + *pcchMultiSz = cchResult; + } + + pwzResult = NULL; + +LExit: + ReleaseStr(pwzResult); + + return hr; +} + +/**************************************************************************** +MultiSzReplaceString - replaces string at the specified index with a new one + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzReplaceString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzString + ) +{ + Assert(ppwzMultiSz && pwzString && *pwzString); + + HRESULT hr = S_OK; + + hr = MultiSzRemoveString(ppwzMultiSz, dwIndex); + StrExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex); + + hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString); + StrExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex); + +LExit: + return hr; +} + + +/**************************************************************************** +wcsistr - case insensitive find a substring + +****************************************************************************/ +extern "C" LPCWSTR DAPI wcsistr( + __in_z LPCWSTR wzString, + __in_z LPCWSTR wzCharSet + ) +{ + LPCWSTR wzSource = wzString; + LPCWSTR wzSearch = NULL; + SIZE_T cchSourceIndex = 0; + + // Walk through wzString (the source string) one character at a time + while (*wzSource) + { + cchSourceIndex = 0; + wzSearch = wzCharSet; + + // Look ahead in the source string until we get a full match or we hit the end of the source + while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch)) + { + ++cchSourceIndex; + ++wzSearch; + } + + // If we found it, return the point that we found it at + if (L'\0' == *wzSearch) + { + return wzSource; + } + + // Walk ahead one character + ++wzSource; + } + + return NULL; +} + +/**************************************************************************** +StrStringToInt16 - converts a string to a signed 16-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out SHORT* psOut + ) +{ + HRESULT hr = S_OK; + LONGLONG ll = 0; + + hr = StrStringToInt64(wzIn, cchIn, &ll); + StrExitOnFailure(hr, "Failed to parse int64."); + + if (SHORT_MAX < ll || SHORT_MIN > ll) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *psOut = (SHORT)ll; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt16 - converts a string to an unsigned 16-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out USHORT* pusOut + ) +{ + HRESULT hr = S_OK; + ULONGLONG ull = 0; + + hr = StrStringToUInt64(wzIn, cchIn, &ull); + StrExitOnFailure(hr, "Failed to parse uint64."); + + if (USHORT_MAX < ull) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *pusOut = (USHORT)ull; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToInt32 - converts a string to a signed 32-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out INT* piOut + ) +{ + HRESULT hr = S_OK; + LONGLONG ll = 0; + + hr = StrStringToInt64(wzIn, cchIn, &ll); + StrExitOnFailure(hr, "Failed to parse int64."); + + if (INT_MAX < ll || INT_MIN > ll) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *piOut = (INT)ll; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt32 - converts a string to an unsigned 32-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out UINT* puiOut + ) +{ + HRESULT hr = S_OK; + ULONGLONG ull = 0; + + hr = StrStringToUInt64(wzIn, cchIn, &ull); + StrExitOnFailure(hr, "Failed to parse uint64."); + + if (UINT_MAX < ull) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *puiOut = (UINT)ull; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToInt64 - converts a string to a signed 64-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out LONGLONG* pllOut + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + INT iSign = 1; + INT nDigit = 0; + LARGE_INTEGER liValue = { }; + size_t cchString = 0; + + // get string length if not provided + if (0 >= cchIn) + { + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); + StrExitOnRootFailure(hr, "Failed to get length of string."); + + cchIn = (DWORD)cchString; + if (0 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + // check sign + if (L'-' == wzIn[0]) + { + if (1 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + i = 1; + iSign = -1; + } + + // read digits + while (i < cchIn) + { + nDigit = wzIn[i] - L'0'; + if (0 > nDigit || 9 < nDigit) + { + ExitFunction1(hr = E_INVALIDARG); + } + liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign; + + if ((liValue.HighPart ^ iSign) & INT_MIN) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + ++i; + } + + *pllOut = liValue.QuadPart; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt64 - converts a string to an unsigned 64-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out ULONGLONG* pullOut + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + DWORD nDigit = 0; + ULONGLONG ullValue = 0; + ULONGLONG ull = 0; + size_t cchString = 0; + + // get string length if not provided + if (0 >= cchIn) + { + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); + StrExitOnRootFailure(hr, "Failed to get length of string."); + + cchIn = (DWORD)cchString; + if (0 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + // read digits + while (i < cchIn) + { + nDigit = wzIn[i] - L'0'; + if (9 < nDigit) + { + ExitFunction1(hr = E_INVALIDARG); + } + ull = (ULONGLONG)(ullValue * 10 + nDigit); + + if (ull < ullValue) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + ullValue = ull; + ++i; + } + + *pullOut = ullValue; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUpper - alters the given string in-place to be entirely uppercase + +****************************************************************************/ +void DAPI StrStringToUpper( + __inout_z LPWSTR wzIn + ) +{ + ::CharUpperBuffW(wzIn, lstrlenW(wzIn)); +} + +/**************************************************************************** +StrStringToLower - alters the given string in-place to be entirely lowercase + +****************************************************************************/ +void DAPI StrStringToLower( + __inout_z LPWSTR wzIn + ) +{ + ::CharLowerBuffW(wzIn, lstrlenW(wzIn)); +} + +/**************************************************************************** +StrAllocStringToUpperInvariant - creates an upper-case copy of a string. + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocStringToUpperInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE); +} + +/**************************************************************************** +StrAllocStringToLowerInvariant - creates an lower-case copy of a string. + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocStringToLowerInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE); +} + +/**************************************************************************** +StrArrayAllocString - Allocates a string array. + +****************************************************************************/ +extern "C" HRESULT DAPI StrArrayAllocString( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + HRESULT hr = S_OK; + UINT cNewStrArray; + + hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray); + StrExitOnFailure(hr, "Failed to increment the string array element count."); + + hr = MemEnsureArraySize(reinterpret_cast(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE); + StrExitOnFailure(hr, "Failed to allocate memory for the string array."); + + hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource); + StrExitOnFailure(hr, "Failed to allocate and assign the string."); + + *pcStrArray = cNewStrArray; + +LExit: + return hr; +} + +/**************************************************************************** +StrArrayFree - Frees a string array. + +Use ReleaseNullStrArray to nullify the arguments. + +****************************************************************************/ +extern "C" HRESULT DAPI StrArrayFree( + __in_ecount(cStrArray) LPWSTR *rgsczStrArray, + __in UINT cStrArray + ) +{ + HRESULT hr = S_OK; + + for (UINT i = 0; i < cStrArray; ++i) + { + if (NULL != rgsczStrArray[i]) + { + hr = StrFree(rgsczStrArray[i]); + StrExitOnFailure(hr, "Failed to free the string at index %u.", i); + } + } + + hr = MemFree(rgsczStrArray); + StrExitOnFailure(hr, "Failed to free memory for the string array."); + +LExit: + return hr; +} + +/**************************************************************************** +StrSplitAllocArray - Splits a string into an array. + +****************************************************************************/ +extern "C" HRESULT DAPI StrSplitAllocArray( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzDelim + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCopy = NULL; + LPWSTR wzContext = NULL; + + // Copy wzSource so it is not modified. + hr = StrAllocString(&sczCopy, wzSource, 0); + StrExitOnFailure(hr, "Failed to copy the source string."); + + for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) + { + hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0); + StrExitOnFailure(hr, "Failed to add the string to the string array."); + } + +LExit: + ReleaseStr(sczCopy); + + return hr; +} + +/**************************************************************************** +StrAllocStringMapInvariant - helper function for the ToUpper and ToLower. + +Note: Assumes source and destination buffers will be the same. +****************************************************************************/ +static HRESULT StrAllocStringMapInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in DWORD dwMapFlags + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(pscz, wzSource, cchSource); + StrExitOnFailure(hr, "Failed to allocate a copy of the source string."); + + if (0 == cchSource) + { + // Need the actual string size for LCMapString. This includes the null-terminator + // but LCMapString doesn't care either way. + hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "Failed to get the length of the string."); + } + else if (INT_MAX < cchSource) + { + StrExitOnRootFailure(hr = E_INVALIDARG, "Source string is too long: %Iu", cchSource); + } + + // Convert the copy of the string to upper or lower case in-place. + if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, static_cast(cchSource), *pscz, static_cast(cchSource))) + { + StrExitWithLastError(hr, "Failed to convert the string case."); + } + +LExit: + return hr; +} + +/**************************************************************************** +StrSecureZeroString - zeroes out string to the make sure the contents +don't remain in memory. + +****************************************************************************/ +extern "C" DAPI_(HRESULT) StrSecureZeroString( + __in LPWSTR pwz + ) +{ + HRESULT hr = S_OK; + SIZE_T cch; + + if (pwz) + { + cch = MemSize(pwz); + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "Failed to get size of string"); + } + else + { + SecureZeroMemory(pwz, cch); + } + } + +LExit: + return hr; +} + +/**************************************************************************** +StrSecureZeroFreeString - zeroes out string to the make sure the contents +don't remain in memory, then frees the string. + +****************************************************************************/ +extern "C" DAPI_(HRESULT) StrSecureZeroFreeString( + __in LPWSTR pwz + ) +{ + HRESULT hr = S_OK; + + hr = StrSecureZeroString(pwz); + ReleaseStr(pwz); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define SvcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) +#define SvcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) +#define SvcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) +#define SvcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) +#define SvcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SVCUTIL, e, x, s, __VA_ARGS__) +#define SvcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SVCUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +SvcQueryConfig - queries the configuration of a service + +********************************************************************/ +extern "C" HRESULT DAPI SvcQueryConfig( + __in SC_HANDLE sch, + __out QUERY_SERVICE_CONFIGW** ppConfig + ) +{ + HRESULT hr = S_OK; + QUERY_SERVICE_CONFIGW* pConfig = NULL; + DWORD cbConfig = 0; + + if (!::QueryServiceConfigW(sch, NULL, 0, &cbConfig)) + { + DWORD er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + pConfig = static_cast(MemAlloc(cbConfig, TRUE)); + SvcExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration."); + + if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig)) + { + SvcExitWithLastError(hr, "Failed to read service configuration."); + } + } + else + { + SvcExitOnWin32Error(er, hr, "Failed to query service configuration."); + } + } + + *ppConfig = pConfig; + pConfig = NULL; + +LExit: + ReleaseMem(pConfig); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ThmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) +#define ThmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) +#define ThmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) +#define ThmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) +#define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__) +#define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__) + +// from CommCtrl.h +#ifndef BS_COMMANDLINK +#define BS_COMMANDLINK 0x0000000EL +#endif + +#ifndef BCM_SETNOTE +#define BCM_SETNOTE (BCM_FIRST + 0x0009) +#endif + +#ifndef BCM_SETSHIELD +#define BCM_SETSHIELD (BCM_FIRST + 0x000C) +#endif + +#ifndef LWS_NOPREFIX +#define LWS_NOPREFIX 0x0004 +#endif + +const DWORD THEME_INVALID_ID = 0xFFFFFFFF; +const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF; +const DWORD GROW_FONT_INSTANCES = 3; +const DWORD GROW_WINDOW_TEXT = 250; +const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink"; +const LPCWSTR THEME_WC_PANEL = L"ThemePanel"; +const LPCWSTR THEME_WC_STATICOWNERDRAW = L"ThemeStaticOwnerDraw"; + +static Gdiplus::GdiplusStartupInput vgsi; +static Gdiplus::GdiplusStartupOutput vgso = { }; +static ULONG_PTR vgdiToken = 0; +static ULONG_PTR vgdiHookToken = 0; +static HMODULE vhHyperlinkRegisteredModule = NULL; +static HMODULE vhPanelRegisteredModule = NULL; +static HMODULE vhStaticOwnerDrawRegisteredModule = NULL; +static WNDPROC vpfnStaticOwnerDrawBaseWndProc = NULL; +static HMODULE vhModuleMsftEdit = NULL; +static HMODULE vhModuleRichEd = NULL; +static HCURSOR vhCursorHand = NULL; + +enum INTERNAL_CONTROL_STYLE +{ + INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001, + INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002, + INTERNAL_CONTROL_STYLE_DISABLED = 0x0004, + INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008, + INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010, +}; + +struct MEMBUFFER_FOR_RICHEDIT +{ + BYTE* rgbData; + DWORD cbData; + + DWORD iData; +}; + + +// prototypes +static HRESULT RegisterWindowClasses( + __in_opt HMODULE hModule + ); +static HRESULT ParseTheme( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMDocument* pixd, + __out THEME** ppTheme + ); +static HRESULT ParseImage( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HBITMAP* phImage + ); +static HRESULT ParseIcon( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HICON* phIcon + ); +static HRESULT ParseWindow( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ); +static HRESULT GetFontColor( + __in IXMLDOMNode* pixn, + __in_z LPCWSTR wzAttributeName, + __out COLORREF* pColorRef, + __out DWORD* pdwSystemColor + ); +static HRESULT ParseFonts( + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ); +static HRESULT ParsePages( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ); +static HRESULT ParseImageLists( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ); +static HRESULT ParseControls( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_opt THEME_PAGE* pPage + ); +static HRESULT ParseControl( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in BOOL fSkipDimensions, + __in_opt THEME_PAGE* pPage + ); +static HRESULT ParseActions( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseColumns( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseRadioButtons( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in THEME_PAGE* pPage + ); +static HRESULT ParseTabs( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseText( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren +); +static HRESULT ParseTooltips( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren + ); +static HRESULT ParseNotes( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __out BOOL* pfAnyChildren + ); +static HRESULT StopBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ); +static HRESULT StartBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ); +static HRESULT EnsureFontInstance( + __in THEME* pTheme, + __in THEME_FONT* pFont, + __out THEME_FONT_INSTANCE** ppFontInstance + ); +static HRESULT FindImageList( + __in THEME* pTheme, + __in_z LPCWSTR wzImageListName, + __out HIMAGELIST *phImageList + ); +static HRESULT LoadControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ); +static HRESULT ShowControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId, + __out_opt HWND* phwndFocus + ); +static HRESULT ShowControls( + __in THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId + ); +static HRESULT DrawButton( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static void DrawControlText( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl, + __in BOOL fCentered, + __in BOOL fDrawFocusRect + ); +static HRESULT DrawHyperlink( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static HRESULT DrawImage( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static HRESULT DrawProgressBar( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static BOOL DrawHoverControl( + __in THEME* pTheme, + __in BOOL fHover + ); +static DWORD CALLBACK RichEditStreamFromFileHandleCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG *pcb + ); +static DWORD CALLBACK RichEditStreamFromMemoryCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG *pcb + ); +static void FreeFontInstance( + __in THEME_FONT_INSTANCE* pFontInstance + ); +static void FreeFont( + __in THEME_FONT* pFont + ); +static void FreePage( + __in THEME_PAGE* pPage + ); +static void FreeControl( + __in THEME_CONTROL* pControl + ); +static void FreeConditionalText( + __in THEME_CONDITIONAL_TEXT* pConditionalText + ); +static void FreeImageList( + __in THEME_IMAGELIST* pImageList + ); +static void FreeAction( + __in THEME_ACTION* pAction + ); +static void FreeColumn( + __in THEME_COLUMN* pColumn + ); +static void FreeTab( + __in THEME_TAB* pTab + ); +static void CALLBACK OnBillboardTimer( + __in THEME* pTheme, + __in HWND hwnd, + __in UINT_PTR idEvent + ); +static void OnBrowseDirectory( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_ACTION* pAction + ); +static BOOL OnButtonClicked( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_CONTROL* pControl + ); +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ); +static void OnNcCreate( + __in THEME* pTheme, + __in HWND hWnd, + __in LPARAM lParam + ); +static HRESULT OnRichEditEnLink( + __in LPARAM lParam, + __in HWND hWndRichEdit, + __in HWND hWnd + ); +static BOOL ControlIsType( + __in const THEME* pTheme, + __in DWORD dwControl, + __in THEME_CONTROL_TYPE type + ); +static const THEME_CONTROL* FindControlFromHWnd( + __in const THEME* pTheme, + __in HWND hWnd, + __in_opt const THEME_CONTROL* pParentControl = NULL + ); +static void GetControlDimensions( + __in const THEME_CONTROL* pControl, + __in const RECT* prcParent, + __out int* piWidth, + __out int* piHeight, + __out int* piX, + __out int* piY + ); +// Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns +// calculates final width of each column (storing result in each column's nWidth value) +static HRESULT SizeListViewColumns( + __inout THEME_CONTROL* pControl + ); +static LRESULT CALLBACK ControlGroupDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static LRESULT CALLBACK PanelWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static LRESULT CALLBACK StaticOwnerDrawWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static HRESULT LocalizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const WIX_LOCALIZATION *pWixLoc + ); +static HRESULT LocalizeControl( + __in THEME_CONTROL* pControl, + __in const WIX_LOCALIZATION *pWixLoc + ); +static HRESULT LoadControlsString( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in HMODULE hResModule + ); +static HRESULT LoadControlString( + __in THEME_CONTROL* pControl, + __in HMODULE hResModule + ); +static void ResizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const RECT* prcParent + ); +static void ResizeControl( + __in THEME_CONTROL* pControl, + __in const RECT* prcParent + ); +static void ScaleThemeFromWindow( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ); +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle + ); +static void ScaleControls( + __in THEME* pTheme, + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in UINT nDpi + ); +static void ScaleControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in UINT nDpi + ); +static void GetControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __out DWORD** ppcControls, + __out THEME_CONTROL*** pprgControls + ); +static void GetControls( + __in const THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __out DWORD& cControls, + __out THEME_CONTROL*& rgControls + ); +static void UnloadControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls + ); + + +// Public functions. + +DAPI_(HRESULT) ThemeInitialize( + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + INITCOMMONCONTROLSEX icex = { }; + + DpiuInitialize(); + + hr = XmlInitialize(); + ThmExitOnFailure(hr, "Failed to initialize XML."); + + hr = RegisterWindowClasses(hModule); + ThmExitOnFailure(hr, "Failed to register theme window classes."); + + // Initialize GDI+ and common controls. + vgsi.SuppressBackgroundThread = TRUE; + + hr = GdipInitialize(&vgsi, &vgdiToken, &vgso); + ThmExitOnFailure(hr, "Failed to initialize GDI+."); + + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS; + ::InitCommonControlsEx(&icex); + + (*vgso.NotificationHook)(&vgdiHookToken); + +LExit: + return hr; +} + + +DAPI_(void) ThemeUninitialize() +{ + if (vhModuleMsftEdit) + { + ::FreeLibrary(vhModuleMsftEdit); + vhModuleMsftEdit = NULL; + } + + if (vhModuleRichEd) + { + ::FreeLibrary(vhModuleRichEd); + vhModuleRichEd = NULL; + } + + if (vhHyperlinkRegisteredModule) + { + ::UnregisterClassW(THEME_WC_HYPERLINK, vhHyperlinkRegisteredModule); + vhHyperlinkRegisteredModule = NULL; + } + + if (vhPanelRegisteredModule) + { + ::UnregisterClassW(THEME_WC_PANEL, vhPanelRegisteredModule); + vhPanelRegisteredModule = NULL; + } + + if (vhStaticOwnerDrawRegisteredModule) + { + ::UnregisterClassW(THEME_WC_STATICOWNERDRAW, vhStaticOwnerDrawRegisteredModule); + vhStaticOwnerDrawRegisteredModule = NULL; + vpfnStaticOwnerDrawBaseWndProc = NULL; + } + + if (vgdiToken) + { + GdipUninitialize(vgdiToken); + vgdiToken = 0; + } + + XmlUninitialize(); + DpiuUninitialize(); +} + + +DAPI_(HRESULT) ThemeLoadFromFile( + __in_z LPCWSTR wzThemeFile, + __out THEME** ppTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixd = NULL; + LPWSTR sczRelativePath = NULL; + + hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd); + ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = PathGetDirectory(wzThemeFile, &sczRelativePath); + ThmExitOnFailure(hr, "Failed to get relative path from theme file."); + + hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme); + ThmExitOnFailure(hr, "Failed to parse theme."); + +LExit: + ReleaseStr(sczRelativePath); + ReleaseObject(pixd); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadFromResource( + __in_opt HMODULE hModule, + __in_z LPCSTR szResource, + __out THEME** ppTheme + ) +{ + HRESULT hr = S_OK; + LPVOID pvResource = NULL; + DWORD cbResource = 0; + LPWSTR sczXml = NULL; + IXMLDOMDocument* pixd = NULL; + + hr = ResReadData(hModule, szResource, &pvResource, &cbResource); + ThmExitOnFailure(hr, "Failed to read theme from resource."); + + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); + ThmExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + + hr = XmlLoadDocument(sczXml, &pixd); + ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = ParseTheme(hModule, NULL, pixd, ppTheme); + ThmExitOnFailure(hr, "Failed to parse theme."); + +LExit: + ReleaseObject(pixd); + ReleaseStr(sczXml); + + return hr; +} + + +DAPI_(void) ThemeFree( + __in THEME* pTheme + ) +{ + if (pTheme) + { + for (DWORD i = 0; i < pTheme->cFonts; ++i) + { + FreeFont(pTheme->rgFonts + i); + } + + for (DWORD i = 0; i < pTheme->cPages; ++i) + { + FreePage(pTheme->rgPages + i); + } + + for (DWORD i = 0; i < pTheme->cImageLists; ++i) + { + FreeImageList(pTheme->rgImageLists + i); + } + + for (DWORD i = 0; i < pTheme->cControls; ++i) + { + FreeControl(pTheme->rgControls + i); + } + + ReleaseMem(pTheme->rgControls); + ReleaseMem(pTheme->rgPages); + ReleaseMem(pTheme->rgFonts); + + if (pTheme->hImage) + { + ::DeleteBitmap(pTheme->hImage); + } + + ReleaseStr(pTheme->sczCaption); + ReleaseMem(pTheme); + } +} + +DAPI_(HRESULT) ThemeRegisterVariableCallbacks( + __in THEME* pTheme, + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + + pTheme->pfnEvaluateCondition = pfnEvaluateCondition; + pTheme->pfnFormatString = pfnFormatString; + pTheme->pfnGetNumericVariable = pfnGetNumericVariable; + pTheme->pfnSetNumericVariable = pfnSetNumericVariable; + pTheme->pfnGetStringVariable = pfnGetStringVariable; + pTheme->pfnSetStringVariable = pfnSetStringVariable; + pTheme->pvVariableContext = pvContext; + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeCreateParentWindow( + __in THEME* pTheme, + __in DWORD dwExStyle, + __in LPCWSTR szClassName, + __in LPCWSTR szWindowName, + __in DWORD dwStyle, + __in int x, + __in int y, + __in_opt HWND hwndParent, + __in_opt HINSTANCE hInstance, + __in_opt LPVOID lpParam, + __in THEME_WINDOW_INITIAL_POSITION initialPosition, + __out_opt HWND* phWnd + ) +{ + HRESULT hr = S_OK; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + POINT pt = { }; + RECT* pMonitorRect = NULL; + HMENU hMenu = NULL; + HWND hWnd = NULL; + + if (pTheme->hwndParent) + { + ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded."); + } + + if (THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES == initialPosition) + { + pt.x = x; + pt.y = y; + hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); + if (SUCCEEDED(hr)) + { + pMonitorRect = &pMonitorContext->mi.rcWork; + if (pMonitorContext->nDpi != pTheme->nDpi) + { + ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top, dwStyle, NULL != hMenu, dwExStyle); + } + + x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWindowWidth) / 2; + y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nWindowHeight) / 2; + } + else + { + hr = S_OK; + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + } + } + + hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, hwndParent, hMenu, hInstance, lpParam); + ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window."); + ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE."); + AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window."); + + if (phWnd) + { + *phWnd = hWnd; + } + +LExit: + ReleaseMem(pMonitorContext); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadControls( + __in THEME* pTheme, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ) +{ + HRESULT hr = S_OK; + + if (!pTheme->hwndParent) + { + ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created."); + } + + hr = LoadControls(pTheme, NULL, rgAssignControlIds, cAssignControlIds); + +LExit: + return hr; +} + + +DAPI_(void) ThemeUnloadControls( + __in THEME* pTheme + ) +{ + UnloadControls(pTheme->cControls, pTheme->rgControls); + + pTheme->hwndHover = NULL; + pTheme->hwndParent = NULL; +} + +DAPI_(HRESULT) ThemeLocalize( + __in THEME *pTheme, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCaption = NULL; + + hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption); + ThmExitOnFailure(hr, "Failed to localize theme caption."); + + if (pTheme->pfnFormatString) + { + hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext); + if (SUCCEEDED(hr)) + { + hr = ThemeUpdateCaption(pTheme, sczCaption); + } + } + + hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc); + +LExit: + ReleaseStr(sczCaption); + + return hr; +} + +/******************************************************************** + ThemeLoadStrings - Loads string resources. + Must be called after loading a theme and before calling + ThemeLoadControls. +*******************************************************************/ +DAPI_(HRESULT) ThemeLoadStrings( + __in THEME* pTheme, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + + if (UINT_MAX != pTheme->uStringId) + { + hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption); + ThmExitOnFailure(hr, "Failed to load theme caption."); + } + + hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule); + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeLoadRichEditFromFile( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCWSTR wzFileName, + __in HMODULE hModule + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFile = NULL; + HANDLE hFile = INVALID_HANDLE_VALUE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + hr = PathRelativeToModule(&sczFile, wzFileName, hModule); + ThmExitOnFailure(hr, "Failed to read resource data."); + + hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ThmExitWithLastError(hr, "Failed to open RTF file."); + } + else + { + LONGLONG llRtfSize; + hr = FileSizeByHandle(hFile, &llRtfSize); + if (SUCCEEDED(hr)) + { + ::SendMessageW(hWnd, EM_EXLIMITTEXT, 0, static_cast(llRtfSize)); + } + + EDITSTREAM es = { }; + es.pfnCallback = RichEditStreamFromFileHandleCallback; + es.dwCookie = reinterpret_cast(hFile); + + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); + hr = es.dwError; + ThmExitOnFailure(hr, "Failed to update RTF stream."); + } + +LExit: + ReleaseStr(sczFile); + ReleaseFile(hFile); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadRichEditFromResource( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ThemeLoadRichEditFromResourceToHWnd(hWnd, szResourceName, hModule); +} + +DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( + __in HWND hWnd, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ) +{ + HRESULT hr = S_OK; + MEMBUFFER_FOR_RICHEDIT buffer = { }; + EDITSTREAM es = { }; + + hr = ResReadData(hModule, szResourceName, reinterpret_cast(&buffer.rgbData), &buffer.cbData); + ThmExitOnFailure(hr, "Failed to read resource data."); + + es.pfnCallback = RichEditStreamFromMemoryCallback; + es.dwCookie = reinterpret_cast(&buffer); + + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); + hr = es.dwError; + ThmExitOnFailure(hr, "Failed to update RTF stream."); + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeHandleKeyboardMessage( + __in_opt THEME* pTheme, + __in HWND /*hWnd*/, + __in MSG* pMsg + ) +{ + return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE; +} + + +extern "C" LRESULT CALLBACK ThemeDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + RECT rcParent = { }; + RECT *pRect = NULL; + + if (pTheme) + { + switch (uMsg) + { + case WM_NCCREATE: + if (pTheme->hwndParent) + { + AssertSz(FALSE, "WM_NCCREATE called multiple times"); + } + else + { + OnNcCreate(pTheme, hWnd, lParam); + } + break; + + case WM_NCHITTEST: + if (pTheme->dwStyle & WS_POPUP) + { + return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control. + } + break; + + case WM_DPICHANGED: + if (OnDpiChanged(pTheme, wParam, lParam)) + { + return 0; + } + break; + + case WM_SIZING: + if (pTheme->fAutoResize) + { + pRect = reinterpret_cast(lParam); + if (pRect->right - pRect->left < pTheme->nMinimumWidth) + { + if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) + { + pRect->left = pRect->right - pTheme->nMinimumWidth; + } + else + { + pRect->right = pRect->left + pTheme->nMinimumWidth; + } + } + if (pRect->bottom - pRect->top < pTheme->nMinimumHeight) + { + if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT) + { + pRect->bottom = pRect->top + pTheme->nMinimumHeight; + } + else + { + pRect->top = pRect->bottom - pTheme->nMinimumHeight; + } + } + + return TRUE; + } + break; + + case WM_SIZE: + if (pTheme->fAutoResize || pTheme->fForceResize) + { + pTheme->fForceResize = FALSE; + ::GetClientRect(pTheme->hwndParent, &rcParent); + ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent); + return 0; + } + break; + } + } + + return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); +} + + +DAPI_(void) ThemeGetPageIds( + __in const THEME* pTheme, + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, + __inout_ecount(cGetPages) DWORD* rgdwPageIds, + __in DWORD cGetPages + ) +{ + for (DWORD i = 0; i < cGetPages; ++i) + { + LPCWSTR wzFindName = rgwzFindNames[i]; + for (DWORD j = 0; j < pTheme->cPages; ++j) + { + LPCWSTR wzPageName = pTheme->rgPages[j].sczName; + if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1)) + { + rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid). + break; + } + } + } +} + + +DAPI_(THEME_PAGE*) ThemeGetPage( + __in const THEME* pTheme, + __in DWORD dwPage + ) +{ + DWORD iPage = dwPage - 1; + THEME_PAGE* pPage = NULL; + + if (iPage < pTheme->cPages) + { + pPage = pTheme->rgPages + iPage; + } + + return pPage; +} + + +DAPI_(HRESULT) ThemeShowPage( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow + ) +{ + return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT); +} + + +DAPI_(HRESULT) ThemeShowPageEx( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow, + __in THEME_SHOW_PAGE_REASON reason + ) +{ + HRESULT hr = S_OK; + BOOL fHide = SW_HIDE == nCmdShow; + BOOL fSaveEditboxes = FALSE; + THEME_SAVEDVARIABLE* pSavedVariable = NULL; + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage); + + if (pPage) + { + if (fHide) + { + switch (reason) + { + case THEME_SHOW_PAGE_REASON_DEFAULT: + // Set the variables in the loop below. + fSaveEditboxes = TRUE; + break; + case THEME_SHOW_PAGE_REASON_CANCEL: + if (pPage->cSavedVariables && pTheme->pfnSetStringVariable) + { + // Best effort to cancel any changes to the variables. + for (DWORD v = 0; v < pPage->cSavedVariables; ++v) + { + pSavedVariable = pPage->rgSavedVariables + v; + if (pSavedVariable->wzName) + { + pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, FALSE, pTheme->pvVariableContext); + } + } + } + break; + } + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason) + { + pPage->cSavedVariables = 0; + if (pPage->rgSavedVariables) + { + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); + } + } + + pTheme->dwCurrentPageId = 0; + } + else + { + if (THEME_SHOW_PAGE_REASON_REFRESH == reason) + { + fSaveEditboxes = TRUE; + } + else + { + hr = MemEnsureArraySize(reinterpret_cast(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices); + ThmExitOnFailure(hr, "Failed to allocate memory for saved variables."); + + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); + pPage->cSavedVariables = pPage->cControlIndices; + + // Save the variables in the loop below. + } + + pTheme->dwCurrentPageId = dwPage; + } + } + + hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage); + ThmExitOnFailure(hr, "Failed to show page controls."); + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeControlExists( + __in const THEME* pTheme, + __in DWORD dwControl + ) +{ + BOOL fExists = FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + fExists = (pControl && hWnd == pControl->hWnd); + } + + return fExists; +} + + +DAPI_(void) ThemeControlEnable( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fEnable + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED); + ::EnableWindow(hWnd, fEnable); + + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) + { + ::ShowWindow(hWnd, fEnable ? SW_SHOW : SW_HIDE); + } + } +} + + +DAPI_(BOOL) ThemeControlEnabled( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + return pControl && !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); +} + + +DAPI_(void) ThemeControlElevates( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fElevates + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + ::SendMessageW(hWnd, BCM_SETSHIELD, 0, fElevates); +} + + +DAPI_(void) ThemeShowControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + ::ShowWindow(hWnd, nCmdShow); + + // Save the control's visible state. + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN); + } +} + + +DAPI_(void) ThemeShowControlEx( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + ShowControl(pTheme, pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL); + } +} + + +DAPI_(BOOL) ThemeControlVisible( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ::IsWindowVisible(hWnd); +} + + +DAPI_(BOOL) ThemePostControlMessage( + __in THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (!::PostMessageW(hWnd, Msg, wParam, lParam)) + { + er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + } + + return SUCCEEDED(hr); +} + + +DAPI_(LRESULT) ThemeSendControlMessage( + __in const THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ::SendMessageW(hWnd, Msg, wParam, lParam); +} + + +DAPI_(HRESULT) ThemeDrawBackground( + __in THEME* pTheme, + __in PAINTSTRUCT* pps + ) +{ + HRESULT hr = S_FALSE; + + if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase) + { + HDC hdcMem = ::CreateCompatibleDC(pps->hdc); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pTheme->hImage)); + DWORD dwSourceWidth = pTheme->nDefaultDpiWidth; + DWORD dwSourceHeight = pTheme->nDefaultDpiHeight; + + ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + + hr = S_OK; + } + + return hr; +} + + +DAPI_(HRESULT) ThemeDrawControl( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis + ) +{ + HRESULT hr = S_OK; + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, pdis->hwndItem); + + AssertSz(pControl, "Expected control window from owner draw window."); + AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window."); + AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width."); + AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height."); + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_BUTTON: + hr = DrawButton(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw button."); + break; + + case THEME_CONTROL_TYPE_HYPERLINK: + hr = DrawHyperlink(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw hyperlink."); + break; + + case THEME_CONTROL_TYPE_IMAGE: + hr = DrawImage(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw image."); + break; + + case THEME_CONTROL_TYPE_PROGRESSBAR: + hr = DrawProgressBar(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw progress bar."); + break; + + default: + hr = E_UNEXPECTED; + ThmExitOnRootFailure(hr, "Did not specify an owner draw control to draw."); + } + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeHoverControl( + __in THEME* pTheme, + __in HWND hwndParent, + __in HWND hwndControl + ) +{ + BOOL fHovered = FALSE; + if (hwndControl != pTheme->hwndHover) + { + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) + { + DrawHoverControl(pTheme, FALSE); + } + + pTheme->hwndHover = hwndControl; + + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) + { + fHovered = DrawHoverControl(pTheme, TRUE); + } + } + + return fHovered; +} + + +DAPI_(BOOL) ThemeIsControlChecked( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return BST_CHECKED == ::SendMessageW(hWnd, BM_GETCHECK, 0, 0); +} + + +DAPI_(BOOL) ThemeSetControlColor( + __in THEME* pTheme, + __in HDC hdc, + __in HWND hWnd, + __out HBRUSH* phBackgroundBrush + ) +{ + THEME_FONT* pFont = NULL; + BOOL fHasBackground = FALSE; + + *phBackgroundBrush = NULL; + + if (hWnd == pTheme->hwndParent) + { + pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId; + } + else + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + pFont = (!pControl || THEME_INVALID_ID == pControl->dwFontId) ? NULL : pTheme->rgFonts + pControl->dwFontId; + } + + if (pFont) + { + if (pFont->hForeground) + { + ::SetTextColor(hdc, pFont->crForeground); + } + + if (pFont->hBackground) + { + ::SetBkColor(hdc, pFont->crBackground); + + *phBackgroundBrush = pFont->hBackground; + fHasBackground = TRUE; + } + else + { + ::SetBkMode(hdc, TRANSPARENT); + *phBackgroundBrush = static_cast(::GetStockObject(NULL_BRUSH)); + fHasBackground = TRUE; + } + } + + return fHasBackground; +} + + +DAPI_(HRESULT) ThemeSetProgressControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwProgressPercentage + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl && THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) + { + DWORD dwCurrentProgress = LOWORD(pControl->dwData); + + if (dwCurrentProgress != dwProgressPercentage) + { + DWORD dwColor = HIWORD(pControl->dwData); + pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor); + + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW) + { + if (!::InvalidateRect(hWnd, NULL, FALSE)) + { + ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); + } + } + else + { + ::SendMessageW(hWnd, PBM_SETPOS, dwProgressPercentage, 0); + } + + hr = S_OK; + } + else + { + hr = S_FALSE; + } + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeSetProgressControlColor( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwColorIndex + ) +{ + HRESULT hr = S_FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + + // Only set color on owner draw progress bars. + if (pControl && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)) + { + DWORD dwCurrentColor = HIWORD(pControl->dwData); + + if (dwCurrentColor != dwColorIndex) + { + DWORD dwCurrentProgress = LOWORD(pControl->dwData); + pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex); + + if (!::InvalidateRect(hWnd, NULL, FALSE)) + { + ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); + } + + hr = S_OK; + } + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeSetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __in_z_opt LPCWSTR wzText + ) +{ + return ThemeSetTextControlEx(pTheme, dwControl, FALSE, wzText); +} + + +DAPI_(HRESULT) ThemeSetTextControlEx( + __in const THEME* pTheme, + __in DWORD dwControl, + __in BOOL fUpdate, + __in_z_opt LPCWSTR wzText + ) +{ + HRESULT hr = S_OK; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + if (fUpdate) + { + ::ShowWindow(hWnd, SW_HIDE); + } + + if (!::SetWindowTextW(hWnd, wzText)) + { + ThmExitWithLastError(hr, "Failed to set control text."); + } + + if (fUpdate) + { + ::ShowWindow(hWnd, SW_SHOW); + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeGetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __inout_z LPWSTR* psczText + ) +{ + HRESULT hr = S_OK; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + SIZE_T cbSize = 0; + DWORD cchText = 0; + DWORD cchTextRead = 0; + + // Ensure the string has room for at least one character. + hr = StrMaxLength(*psczText, &cbSize); + ThmExitOnFailure(hr, "Failed to get text buffer length."); + + cchText = (DWORD)min(DWORD_MAX, cbSize); + + if (!cchText) + { + cchText = GROW_WINDOW_TEXT; + + hr = StrAlloc(psczText, cchText); + ThmExitOnFailure(hr, "Failed to grow text buffer."); + } + + // Read (and keep growing buffer) until we finally read less than there + // is room in the buffer. + for (;;) + { + cchTextRead = ::GetWindowTextW(hWnd, *psczText, cchText); + if (cchTextRead + 1 < cchText) + { + break; + } + else + { + cchText = cchTextRead + GROW_WINDOW_TEXT; + + hr = StrAlloc(psczText, cchText); + ThmExitOnFailure(hr, "Failed to grow text buffer again."); + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeUpdateCaption( + __in THEME* pTheme, + __in_z LPCWSTR wzCaption + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0); + ThmExitOnFailure(hr, "Failed to update theme caption."); + +LExit: + return hr; +} + + +DAPI_(void) ThemeSetFocus( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hwndFocus = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hwndFocus && !ThemeControlEnabled(pTheme, dwControl)) + { + hwndFocus = ::GetNextDlgTabItem(pTheme->hwndParent, hwndFocus, FALSE); + } + + if (hwndFocus) + { + ::SetFocus(hwndFocus); + } +} + + +DAPI_(void) ThemeShowChild( + __in THEME* pTheme, + __in THEME_CONTROL* pParentControl, + __in DWORD dwIndex + ) +{ + // show one child, hide the rest + for (DWORD i = 0; i < pParentControl->cControls; ++i) + { + THEME_CONTROL* pControl = pParentControl->rgControls + i; + ShowControl(pTheme, pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL); + } +} + + +// Internal functions. + +static HRESULT RegisterWindowClasses( + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + WNDCLASSW wcHyperlink = { }; + WNDCLASSW wcPanel = { }; + WNDCLASSW wcStaticOwnerDraw = { }; + WNDPROC pfnStaticOwnerDrawBaseWndProc = NULL; + + vhCursorHand = ::LoadCursorA(NULL, IDC_HAND); + + // Base the theme hyperlink class on a button but give it the "hand" icon. + if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink)) + { + ThmExitWithLastError(hr, "Failed to get button window class."); + } + + wcHyperlink.lpszClassName = THEME_WC_HYPERLINK; +#pragma prefast(push) +#pragma prefast(disable:25068) + wcHyperlink.hCursor = vhCursorHand; +#pragma prefast(pop) + + if (!::RegisterClassW(&wcHyperlink)) + { + ThmExitWithLastError(hr, "Failed to get button window class."); + } + vhHyperlinkRegisteredModule = hModule; + + // Panel is its own do-nothing class. + wcPanel.lpfnWndProc = PanelWndProc; + wcPanel.hInstance = hModule; + wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW); + wcPanel.lpszClassName = THEME_WC_PANEL; + if (!::RegisterClassW(&wcPanel)) + { + ThmExitWithLastError(hr, "Failed to register window."); + } + vhPanelRegisteredModule = hModule; + + if (!::GetClassInfoW(NULL, WC_STATICW, &wcStaticOwnerDraw)) + { + ThmExitWithLastError(hr, "Failed to get static window class."); + } + + pfnStaticOwnerDrawBaseWndProc = wcStaticOwnerDraw.lpfnWndProc; + wcStaticOwnerDraw.lpfnWndProc = StaticOwnerDrawWndProc; + wcStaticOwnerDraw.hInstance = hModule; + wcStaticOwnerDraw.lpszClassName = THEME_WC_STATICOWNERDRAW; + if (!::RegisterClassW(&wcStaticOwnerDraw)) + { + ThmExitWithLastError(hr, "Failed to register OwnerDraw window class."); + } + vhStaticOwnerDrawRegisteredModule = hModule; + vpfnStaticOwnerDrawBaseWndProc = pfnStaticOwnerDrawBaseWndProc; + + +LExit: + return hr; +} + +static HRESULT ParseTheme( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMDocument* pixd, + __out THEME** ppTheme + ) +{ + static WORD wThemeId = 0; + + HRESULT hr = S_OK; + THEME* pTheme = NULL; + IXMLDOMElement *pThemeElement = NULL; + + hr = pixd->get_documentElement(&pThemeElement); + ThmExitOnFailure(hr, "Failed to get theme element."); + + pTheme = static_cast(MemAlloc(sizeof(THEME), TRUE)); + ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); + + pTheme->wId = ++wThemeId; + pTheme->nDpi = USER_DEFAULT_SCREEN_DPI; + + // Parse the optional background resource image. + hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); + ThmExitOnFailure(hr, "Failed while parsing theme image."); + + // Parse the fonts. + hr = ParseFonts(pThemeElement, pTheme); + ThmExitOnFailure(hr, "Failed to parse theme fonts."); + + // Parse the window element. + hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme); + ThmExitOnFailure(hr, "Failed to parse theme window element."); + + *ppTheme = pTheme; + pTheme = NULL; + +LExit: + ReleaseObject(pThemeElement); + + if (pTheme) + { + ThemeFree(pTheme); + } + + return hr; +} + +static HRESULT ParseImage( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HBITMAP* phImage + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + LPWSTR sczImageFile = NULL; + int iResourceId = 0; + Gdiplus::Bitmap* pBitmap = NULL; + *phImage = NULL; + + hr = XmlGetAttribute(pElement, L"ImageResource", &bstr); + ThmExitOnFailure(hr, "Failed to get image resource attribute."); + + if (S_OK == hr) + { + iResourceId = wcstol(bstr, NULL, 10); + + hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(iResourceId), &pBitmap); + // Don't fail. + } + + ReleaseNullBSTR(bstr); + + // Parse the optional background image from a given file. + if (!pBitmap) + { + hr = XmlGetAttribute(pElement, L"ImageFile", &bstr); + ThmExitOnFailure(hr, "Failed to get image file attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczImageFile); + ThmExitOnFailure(hr, "Failed to combine image file path."); + } + else + { + hr = PathRelativeToModule(&sczImageFile, bstr, hModule); + ThmExitOnFailure(hr, "Failed to get image filename."); + } + + hr = GdipBitmapFromFile(sczImageFile, &pBitmap); + // Don't fail. + } + } + + // If there is an image, convert it into a bitmap handle. + if (pBitmap) + { + Gdiplus::Color black; + Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage); + ThmExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP."); + } + + hr = S_OK; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + ReleaseStr(sczImageFile); + ReleaseBSTR(bstr); + + return hr; +} + + +static HRESULT ParseIcon( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HICON* phIcon + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + LPWSTR sczImageFile = NULL; + int iResourceId = 0; + *phIcon = NULL; + + hr = XmlGetAttribute(pElement, L"IconResource", &bstr); + ThmExitOnFailure(hr, "Failed to get icon resource attribute."); + + if (S_OK == hr) + { + iResourceId = wcstol(bstr, NULL, 10); + + *phIcon = reinterpret_cast(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); + ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon."); + } + else + { + ReleaseNullBSTR(bstr); + + hr = XmlGetAttribute(pElement, L"IconFile", &bstr); + ThmExitOnFailure(hr, "Failed to get icon file attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczImageFile); + ThmExitOnFailure(hr, "Failed to combine image file path."); + } + else + { + hr = PathRelativeToModule(&sczImageFile, bstr, hModule); + ThmExitOnFailure(hr, "Failed to get image filename."); + } + + *phIcon = reinterpret_cast(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)); + ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile); + } + } + +LExit: + ReleaseStr(sczImageFile); + ReleaseBSTR(bstr); + + return hr; +} + + +static HRESULT ParseWindow( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DWORD dwValue = 0; + BSTR bstr = NULL; + LPWSTR sczIconFile = NULL; + + hr = XmlSelectSingleNode(pElement, L"Window", &pixn); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find window element."); + + hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get window AutoResize attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Failed to find window Width attribute."); + } + ThmExitOnFailure(hr, "Failed to get window Width attribute."); + + pTheme->nWidth = pTheme->nDefaultDpiWidth = pTheme->nWindowWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Failed to find window Height attribute."); + } + ThmExitOnFailure(hr, "Failed to get window Height attribute."); + + pTheme->nHeight = pTheme->nDefaultDpiHeight = pTheme->nWindowHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue); + if (S_FALSE == hr) + { + dwValue = 0; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); + + pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue); + if (S_FALSE == hr) + { + dwValue = 0; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); + + pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Failed to find window FontId attribute."); + } + ThmExitOnFailure(hr, "Failed to get window FontId attribute."); + + // Get the optional window icon from a resource. + hr = XmlGetAttribute(pixn, L"IconResource", &bstr); + ThmExitOnFailure(hr, "Failed to get window IconResource attribute."); + + if (S_OK == hr) + { + pTheme->hIcon = ::LoadIconW(hModule, bstr); + ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource."); + + ReleaseNullBSTR(bstr); + } + + // Get the optional window icon from a file. + hr = XmlGetAttribute(pixn, L"IconFile", &bstr); + ThmExitOnFailure(hr, "Failed to get window IconFile attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczIconFile); + ThmExitOnFailure(hr, "Failed to combine icon file path."); + } + else + { + hr = PathRelativeToModule(&sczIconFile, bstr, hModule); + ThmExitOnFailure(hr, "Failed to get icon filename."); + } + + pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); + ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr); + + ReleaseNullBSTR(bstr); + } + + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pTheme->nSourceX)); + if (S_FALSE == hr) + { + pTheme->nSourceX = -1; + } + ThmExitOnFailure(hr, "Failed to get window SourceX attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pTheme->nSourceY)); + if (S_FALSE == hr) + { + pTheme->nSourceY = -1; + } + ThmExitOnFailure(hr, "Failed to get window SourceY attribute."); + + // Parse the optional window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle); + ThmExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute."); + + if (S_FALSE == hr) + { + pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU; + pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED; + } + + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pTheme->uStringId)); + ThmExitOnFailure(hr, "Failed to get window StringId attribute."); + + if (S_FALSE == hr) + { + pTheme->uStringId = UINT_MAX; + + hr = XmlGetAttribute(pixn, L"Caption", &bstr); + ThmExitOnFailure(hr, "Failed to get window Caption attribute."); + + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute."); + } + + hr = StrAllocString(&pTheme->sczCaption, bstr, 0); + ThmExitOnFailure(hr, "Failed to copy window Caption attribute."); + } + + // Parse any image lists. + hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme); + ThmExitOnFailure(hr, "Failed to parse image lists."); + + // Parse the pages. + hr = ParsePages(hModule, wzRelativePath, pixn, pTheme); + ThmExitOnFailure(hr, "Failed to parse theme pages."); + + // Parse the non-paged controls. + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL); + ThmExitOnFailure(hr, "Failed to parse theme controls."); + +LExit: + ReleaseStr(sczIconFile); + ReleaseBSTR(bstr); + ReleaseObject(pixn); + + return hr; +} + + +static HRESULT ParseFonts( + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrName = NULL; + DWORD dwId = 0; + COLORREF crForeground = THEME_INVISIBLE_COLORREF; + COLORREF crBackground = THEME_INVISIBLE_COLORREF; + DWORD dwSystemForegroundColor = FALSE; + DWORD dwSystemBackgroundColor = FALSE; + + hr = XmlSelectNodes(pElement, L"Font", &pixnl); + ThmExitOnFailure(hr, "Failed to find font elements."); + + hr = pixnl->get_length(reinterpret_cast(&pTheme->cFonts)); + ThmExitOnFailure(hr, "Failed to count the number of theme fonts."); + + if (!pTheme->cFonts) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgFonts = static_cast(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE)); + ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = XmlGetAttributeNumber(pixn, L"Id", &dwId); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find font id."); + + if (pTheme->cFonts <= dwId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Invalid theme font id."); + } + + THEME_FONT* pFont = pTheme->rgFonts + dwId; + if (pFont->cFontInstances) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Theme font id duplicated."); + } + + pFont->lfQuality = CLEARTYPE_QUALITY; + + hr = XmlGetText(pixn, &bstrName); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to get font name."); + + hr = StrAllocString(&pFont->sczFaceName, bstrName, 0); + ThmExitOnFailure(hr, "Failed to copy font name."); + + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pFont->lfHeight)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find font height attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&pFont->lfWeight)); + if (S_FALSE == hr) + { + pFont->lfWeight = FW_DONTCARE; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to find font weight attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&pFont->lfUnderline)); + if (E_NOTFOUND == hr) + { + pFont->lfUnderline = FALSE; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to find font underline attribute."); + + hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor); + ThmExitOnFailure(hr, "Failed to find font foreground color."); + + hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor); + ThmExitOnFailure(hr, "Failed to find font background color."); + + pFont->crForeground = crForeground; + if (THEME_INVISIBLE_COLORREF != pFont->crForeground) + { + pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground); + ThmExitOnNull(pFont->hForeground, hr, E_OUTOFMEMORY, "Failed to create text foreground brush."); + } + + pFont->crBackground = crBackground; + if (THEME_INVISIBLE_COLORREF != pFont->crBackground) + { + pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground); + ThmExitOnNull(pFont->hBackground, hr, E_OUTOFMEMORY, "Failed to create text background brush."); + } + + ReleaseNullBSTR(bstrName); + ReleaseNullObject(pixn); + } + ThmExitOnFailure(hr, "Failed to enumerate all fonts."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrName); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT GetFontColor( + __in IXMLDOMNode* pixn, + __in_z LPCWSTR wzAttributeName, + __out COLORREF* pColorRef, + __out DWORD* pdwSystemColor + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + + *pdwSystemColor = 0; + + hr = XmlGetAttribute(pixn, wzAttributeName, &bstr); + if (S_FALSE == hr) + { + *pColorRef = THEME_INVISIBLE_COLORREF; + ExitFunction1(hr = S_OK); + } + ThmExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName); + + if (pdwSystemColor) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1)) + { + *pdwSystemColor = COLOR_BTNFACE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1)) + { + *pdwSystemColor = COLOR_BTNTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1)) + { + *pdwSystemColor = COLOR_GRAYTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1)) + { + *pdwSystemColor = COLOR_HIGHLIGHT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1)) + { + *pdwSystemColor = COLOR_HIGHLIGHTTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1)) + { + *pdwSystemColor = COLOR_HOTLIGHT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1)) + { + *pdwSystemColor = COLOR_WINDOW; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1)) + { + *pdwSystemColor = COLOR_WINDOWTEXT; + } + else + { + *pColorRef = wcstoul(bstr, NULL, 16); + } + + if (*pdwSystemColor) + { + *pColorRef = ::GetSysColor(*pdwSystemColor); + } + } + +LExit: + ReleaseBSTR(bstr); + + return hr; +} + +static HRESULT ParsePages( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrType = NULL; + THEME_PAGE* pPage = NULL; + DWORD iPage = 0; + + hr = XmlSelectNodes(pElement, L"Page", &pixnl); + ThmExitOnFailure(hr, "Failed to find page elements."); + + hr = pixnl->get_length(reinterpret_cast(&pTheme->cPages)); + ThmExitOnFailure(hr, "Failed to count the number of theme pages."); + + if (!pTheme->cPages) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgPages = static_cast(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE)); + ThmExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) + { + pPage = pTheme->rgPages + iPage; + + pPage->wId = static_cast(iPage + 1); + + hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying page Name."); + + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage); + ThmExitOnFailure(hr, "Failed to parse page controls."); + + ++iPage; + + ReleaseNullBSTR(bstrType); + ReleaseNullObject(pixn); + } + ThmExitOnFailure(hr, "Failed to enumerate all pages."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrType); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT ParseImageLists( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnlImageLists = NULL; + IXMLDOMNode* pixnImageList = NULL; + IXMLDOMNodeList* pixnlImages = NULL; + IXMLDOMNode* pixnImage = NULL; + DWORD dwImageListIndex = 0; + DWORD dwImageCount = 0; + HBITMAP hBitmap = NULL; + BITMAP bm = { }; + BSTR bstr = NULL; + DWORD i = 0; + int iRetVal = 0; + + hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists); + ThmExitOnFailure(hr, "Failed to find ImageList elements."); + + hr = pixnlImageLists->get_length(reinterpret_cast(&pTheme->cImageLists)); + ThmExitOnFailure(hr, "Failed to count the number of image lists."); + + if (!pTheme->cImageLists) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgImageLists = static_cast(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE)); + ThmExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists."); + + while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL))) + { + hr = XmlGetAttribute(pixnImageList, L"Name", &bstr); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find ImageList/@Name attribute."); + + hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0); + ThmExitOnFailure(hr, "Failed to make copy of ImageList name."); + + hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages); + ThmExitOnFailure(hr, "Failed to select child Image nodes."); + + hr = pixnlImages->get_length(reinterpret_cast(&dwImageCount)); + ThmExitOnFailure(hr, "Failed to count the number of images in list."); + + if (0 < dwImageCount) + { + i = 0; + while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL))) + { + if (hBitmap) + { + ::DeleteObject(hBitmap); + hBitmap = NULL; + } + hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap); + ThmExitOnFailure(hr, "Failed to parse image: %u", i); + + if (0 == i) + { + ::GetObjectW(hBitmap, sizeof(BITMAP), &bm); + + pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0); + ThmExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list."); + } + + iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL); + if (-1 == iRetVal) + { + ThmExitWithLastError(hr, "Failed to add image %u to image list.", i); + } + + ++i; + } + } + ++dwImageListIndex; + } + +LExit: + if (hBitmap) + { + ::DeleteObject(hBitmap); + } + ReleaseBSTR(bstr); + ReleaseObject(pixnlImageLists); + ReleaseObject(pixnImageList); + ReleaseObject(pixnlImages); + ReleaseObject(pixnImage); + + return hr; +} + +static void GetControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __out DWORD** ppcControls, + __out THEME_CONTROL*** pprgControls + ) +{ + if (pParentControl) + { + *ppcControls = &pParentControl->cControls; + *pprgControls = &pParentControl->rgControls; + } + else + { + *ppcControls = &pTheme->cControls; + *pprgControls = &pTheme->rgControls; + } +} + +static void GetControls( + __in const THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __out DWORD& cControls, + __out THEME_CONTROL*& rgControls + ) +{ + if (pParentControl) + { + cControls = pParentControl->cControls; + rgControls = pParentControl->rgControls; + } + else + { + cControls = pTheme->cControls; + rgControls = pTheme->rgControls; + } +} + +static HRESULT ParseControls( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_opt THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrType = NULL; + DWORD cNewControls = 0; + DWORD iControl = 0; + DWORD iPageControl = 0; + DWORD* pcControls = NULL; + THEME_CONTROL** prgControls = NULL; + + GetControls(pTheme, pParentControl, &pcControls, &prgControls); + + hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage); + ThmExitOnFailure(hr, "Failed to parse radio buttons."); + + hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl); + ThmExitOnFailure(hr, "Failed to find control elements."); + + hr = pixnl->get_length(reinterpret_cast(&cNewControls)); + ThmExitOnFailure(hr, "Failed to count the number of theme controls."); + + if (!cNewControls) + { + ExitFunction1(hr = S_OK); + } + + hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls); + ThmExitOnFailure(hr, "Failed to reallocate theme controls."); + + cNewControls += *pcControls; + + if (pPage) + { + iPageControl = pPage->cControlIndices; + pPage->cControlIndices += cNewControls; + } + + iControl = *pcControls; + *pcControls = cNewControls; + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) + { + THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN; + + if (!bstrType) + { + hr = E_UNEXPECTED; + ThmExitOnFailure(hr, "Null element encountered!"); + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1)) + { + type = THEME_CONTROL_TYPE_BILLBOARD; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1)) + { + type = THEME_CONTROL_TYPE_BUTTON; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1)) + { + type = THEME_CONTROL_TYPE_CHECKBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1)) + { + type = THEME_CONTROL_TYPE_COMBOBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1)) + { + type = THEME_CONTROL_TYPE_COMMANDLINK; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1)) + { + type = THEME_CONTROL_TYPE_EDITBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1)) + { + type = THEME_CONTROL_TYPE_HYPERLINK; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1)) + { + type = THEME_CONTROL_TYPE_HYPERTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1)) + { + type = THEME_CONTROL_TYPE_IMAGE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1)) + { + type = THEME_CONTROL_TYPE_LABEL; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1)) + { + type = THEME_CONTROL_TYPE_LISTVIEW; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1)) + { + type = THEME_CONTROL_TYPE_PANEL; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1)) + { + type = THEME_CONTROL_TYPE_PROGRESSBAR; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1)) + { + type = THEME_CONTROL_TYPE_RICHEDIT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1)) + { + type = THEME_CONTROL_TYPE_STATIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1)) + { + type = THEME_CONTROL_TYPE_TAB; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1)) + { + type = THEME_CONTROL_TYPE_TREEVIEW; + } + + if (THEME_CONTROL_TYPE_UNKNOWN != type) + { + THEME_CONTROL* pControl = *prgControls + iControl; + pControl->type = type; + + // billboard children are always the size of the billboard + BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type; + + hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage); + ThmExitOnFailure(hr, "Failed to parse control."); + + if (fBillboardSizing) + { + pControl->nX = pControl->nDefaultDpiX = 0; + pControl->nY = pControl->nDefaultDpiY = 0; + pControl->nWidth = pControl->nDefaultDpiWidth = 0; + pControl->nHeight = pControl->nDefaultDpiHeight = 0; + } + + if (pPage) + { + pControl->wPageId = pPage->wId; + ++iPageControl; + } + + ++iControl; + } + + ReleaseNullBSTR(bstrType); + ReleaseNullObject(pixn); + } + ThmExitOnFailure(hr, "Failed to enumerate all controls."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + + AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls."); + +LExit: + ReleaseBSTR(bstrType); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT ParseControl( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in BOOL fSkipDimensions, + __in_opt THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + DWORD dwValue = 0; + BOOL fValue = FALSE; + BSTR bstrText = NULL; + BOOL fAnyTextChildren = FALSE; + BOOL fAnyNoteChildren = FALSE; + + hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying control Name attribute."); + + hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying control EnableCondition attribute."); + + hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying control VisibleCondition attribute."); + + if (!fSkipDimensions) + { + hr = XmlGetAttributeNumber(pixn, L"X", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control X attribute."); + + pControl->nX = pControl->nDefaultDpiX = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Y", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control Y attribute."); + + pControl->nY = pControl->nDefaultDpiY = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control Height attribute."); + + pControl->nHeight = pControl->nDefaultDpiHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control Width attribute."); + + pControl->nWidth = pControl->nDefaultDpiWidth = dwValue; + } + + // Parse the optional background resource image. + hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage); + ThmExitOnFailure(hr, "Failed while parsing control image."); + + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pControl->nSourceX)); + if (S_FALSE == hr) + { + pControl->nSourceX = -1; + } + ThmExitOnFailure(hr, "Failed when querying control SourceX attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pControl->nSourceY)); + if (S_FALSE == hr) + { + pControl->nSourceY = -1; + } + ThmExitOnFailure(hr, "Failed when querying control SourceY attribute."); + + hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId); + if (S_FALSE == hr) + { + pControl->dwFontId = THEME_INVALID_ID; + } + ThmExitOnFailure(hr, "Failed when querying control FontId attribute."); + + // Parse the optional window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle); + ThmExitOnFailure(hr, "Failed when querying control HexStyle attribute."); + + // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above. + hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control TabStop attribute."); + + if (fValue) + { + pControl->dwStyle |= WS_TABSTOP; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control Visible attribute."); + + if (fValue) + { + pControl->dwStyle |= WS_VISIBLE; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute."); + + if (fValue) + { + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"DisableAutomaticBehavior", &pControl->fDisableVariableFunctionality); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute."); + } + + hr = ParseActions(pixn, pControl); + ThmExitOnFailure(hr, "Failed to parse action nodes of the control."); + + hr = ParseText(pixn, pControl, &fAnyTextChildren); + ThmExitOnFailure(hr, "Failed to parse text nodes of the control."); + + hr = ParseTooltips(pixn, pControl, &fAnyTextChildren); + ThmExitOnFailure(hr, "Failed to parse control Tooltip."); + + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + hr = ParseNotes(pixn, pControl, &fAnyNoteChildren); + ThmExitOnFailure(hr, "Failed to parse note text nodes of the control."); + } + + if (fAnyTextChildren || fAnyNoteChildren) + { + pControl->uStringId = UINT_MAX; + } + else + { + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pControl->uStringId)); + ThmExitOnFailure(hr, "Failed when querying control StringId attribute."); + + if (S_FALSE == hr) + { + pControl->uStringId = UINT_MAX; + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type || THEME_CONTROL_TYPE_PANEL == pControl->type) + { + // Billboards and panels have child elements and we don't want to pick up child element text in the parents. + hr = S_OK; + } + else + { + hr = XmlGetText(pixn, &bstrText); + ThmExitOnFailure(hr, "Failed to get control inner text."); + + if (S_OK == hr) + { + hr = StrAllocString(&pControl->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy control text."); + + ReleaseNullBSTR(bstrText); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + } + } + } + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute."); + + pControl->wBillboardInterval = 5000; + hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue); + if (S_OK == hr && dwValue) + { + pControl->wBillboardInterval = static_cast(dwValue & 0xFFFF); + } + ThmExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute."); + + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); + ThmExitOnFailure(hr, "Failed to parse billboard children."); + } + else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon); + ThmExitOnFailure(hr, "Failed while parsing control icon."); + } + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute."); + + if (fValue) + { + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE; + } + } + } + else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type) + { + hr = XmlGetAttributeNumber(pixn, L"HoverFontId", &pControl->dwFontHoverId); + if (S_FALSE == hr) + { + pControl->dwFontHoverId = THEME_INVALID_ID; + } + ThmExitOnFailure(hr, "Failed when querying control HoverFontId attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId); + if (S_FALSE == hr) + { + pControl->dwFontSelectedId = THEME_INVALID_ID; + } + ThmExitOnFailure(hr, "Failed when querying control SelectedFontId attribute."); + } + else if (THEME_CONTROL_TYPE_LABEL == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= SS_CENTER; + } + ThmExitOnFailure(hr, "Failed when querying Label/@Center attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= SS_NOPREFIX; + } + ThmExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute."); + } + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + // Parse the optional extended window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle); + ThmExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute."); + + hr = XmlGetAttribute(pixn, L"ImageList", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText); + } + + hr = ParseColumns(pixn, pControl); + ThmExitOnFailure(hr, "Failed to parse columns."); + } + else if (THEME_CONTROL_TYPE_PANEL == pControl->type) + { + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); + ThmExitOnFailure(hr, "Failed to parse panel children."); + } + else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type) + { + hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute."); + } + else if (THEME_CONTROL_TYPE_TAB == pControl->type) + { + hr = ParseTabs(pixn, pControl); + ThmExitOnFailure(hr, "Failed to parse tabs"); + } + else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type) + { + pControl->dwStyle |= TVS_DISABLEDRAGDROP; + + hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle &= ~TVS_DISABLEDRAGDROP; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_FULLROWSELECT; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_HASBUTTONS; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_SHOWSELALWAYS; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_LINESATROOT; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_HASLINES; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute."); + } + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseActions( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrType = NULL; + + hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl); + ThmExitOnFailure(hr, "Failed to select child action nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cActions)); + ThmExitOnFailure(hr, "Failed to count the number of action nodes."); + + if (0 < pControl->cActions) + { + MemAllocArray(reinterpret_cast(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions); + ThmExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType))) + { + if (!bstrType) + { + hr = E_UNEXPECTED; + ThmExitOnFailure(hr, "Null element encountered!"); + } + + THEME_ACTION* pAction = pControl->rgActions + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY; + + hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName); + ThmExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE; + + hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName); + ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute."); + + hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel); + if (E_NOTFOUND != hr) + { + ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute."); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW; + } + else + { + hr = E_UNEXPECTED; + ThmExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType); + } + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition); + if (E_NOTFOUND != hr) + { + ThmExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType); + } + + if (!pAction->sczCondition) + { + if (pControl->pDefaultAction) + { + hr = E_INVALIDDATA; + ThmExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName); + } + + pControl->pDefaultAction = pAction; + } + + ++i; + ReleaseNullBSTR(bstrType); + ReleaseObject(pixnChild); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrType); + + return hr; +} + + +static HRESULT ParseColumns( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + DWORD dwValue = 0; + + hr = XmlSelectNodes(pixn, L"Column", &pixnl); + ThmExitOnFailure(hr, "Failed to select child column nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cColumns)); + ThmExitOnFailure(hr, "Failed to count the number of control columns."); + + if (0 < pControl->cColumns) + { + hr = MemAllocArray(reinterpret_cast(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns); + ThmExitOnFailure(hr, "Failed to allocate column structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_COLUMN* pColumn = pControl->ptcColumns + i; + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of column element."); + + hr = XmlGetAttributeNumber(pixnChild, L"Width", &dwValue); + if (S_FALSE == hr) + { + dwValue = 100; + } + ThmExitOnFailure(hr, "Failed to get column width attribute."); + + pColumn->nBaseWidth = pColumn->nDefaultDpiBaseWidth = dwValue; + + hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pColumn->fExpands)); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get expands attribute."); + + hr = StrAllocString(&pColumn->pszName, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy column name."); + + ++i; + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseRadioButtons( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + DWORD cRadioButtons = 0; + DWORD iControl = 0; + DWORD iPageControl = 0; + IXMLDOMNodeList* pixnlRadioButtons = NULL; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnRadioButtons = NULL; + IXMLDOMNode* pixnChild = NULL; + LPWSTR sczName = NULL; + THEME_CONTROL* pControl = NULL; + BOOL fFirst = FALSE; + DWORD* pcControls = NULL; + THEME_CONTROL** prgControls = NULL; + + GetControls(pTheme, pParentControl, &pcControls, &prgControls); + + hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons); + ThmExitOnFailure(hr, "Failed to select RadioButtons nodes."); + + while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL))) + { + hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying RadioButtons Name."); + + hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl); + ThmExitOnFailure(hr, "Failed to select RadioButton nodes."); + + hr = pixnl->get_length(reinterpret_cast(&cRadioButtons)); + ThmExitOnFailure(hr, "Failed to count the number of RadioButton nodes."); + + if (cRadioButtons) + { + if (pPage) + { + iPageControl = pPage->cControlIndices; + pPage->cControlIndices += cRadioButtons; + } + + hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons); + ThmExitOnFailure(hr, "Failed to reallocate theme controls."); + + iControl = *pcControls; + *pcControls += cRadioButtons; + + fFirst = TRUE; + + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + pControl = *prgControls + iControl; + pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON; + + hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage); + ThmExitOnFailure(hr, "Failed to parse control."); + + if (fFirst) + { + pControl->dwStyle |= WS_GROUP; + fFirst = FALSE; + } + + hr = StrAllocString(&pControl->sczVariable, sczName, 0); + ThmExitOnFailure(hr, "Failed to copy radio button variable."); + + if (pPage) + { + pControl->wPageId = pPage->wId; + ++iPageControl; + } + + ++iControl; + } + + if (!fFirst) + { + pControl->fLastRadioButton = TRUE; + } + } + } + +LExit: + ReleaseStr(sczName); + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseObject(pixnlRadioButtons); + ReleaseObject(pixnRadioButtons); + + return hr; +} + + +static HRESULT ParseTabs( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Tab", &pixnl); + ThmExitOnFailure(hr, "Failed to select child tab nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cTabs)); + ThmExitOnFailure(hr, "Failed to count the number of tabs."); + + if (0 < pControl->cTabs) + { + hr = MemAllocArray(reinterpret_cast(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs); + ThmExitOnFailure(hr, "Failed to allocate tab structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of tab element."); + + hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy tab name."); + + ++i; + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseText( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Text", &pixnl); + ThmExitOnFailure(hr, "Failed to select child Text nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalText)); + ThmExitOnFailure(hr, "Failed to count the number of Text nodes."); + + *pfAnyChildren |= 0 < pControl->cConditionalText; + + if (0 < pControl->cConditionalText) + { + MemAllocArray(reinterpret_cast(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText); + ThmExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i; + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying Text/@Condition attribute."); + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of Text element."); + + if (S_OK == hr) + { + if (pConditionalText->sczCondition) + { + hr = StrAllocString(&pConditionalText->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to conditional text."); + + ++i; + } + else + { + if (pControl->sczText) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName); + } + + hr = StrAllocString(&pControl->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to control."); + + // Unconditional text entries aren't stored in the conditional text list. + --pControl->cConditionalText; + } + } + + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseTooltips( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren +) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild); + ThmExitOnFailure(hr, "Failed to select child Tooltip node."); + + if (S_OK == hr) + { + *pfAnyChildren |= TRUE; + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of Tooltip element."); + + if (S_OK == hr) + { + hr = StrAllocString(&pControl->sczTooltip, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy tooltip text to control."); + } + } + +LExit: + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseNotes( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __out BOOL* pfAnyChildren + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Note", &pixnl); + ThmExitOnFailure(hr, "Failed to select child Note nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalNotes)); + ThmExitOnFailure(hr, "Failed to count the number of Note nodes."); + + if (pfAnyChildren) + { + *pfAnyChildren = 0 < pControl->cConditionalNotes; + } + + if (0 < pControl->cConditionalNotes) + { + MemAllocArray(reinterpret_cast(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes); + ThmExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + i; + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying Note/@Condition attribute."); + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of Note element."); + + if (S_OK == hr) + { + if (pConditionalNote->sczCondition) + { + hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to conditional note text."); + + ++i; + } + else + { + if (pControl->sczNote) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName); + } + + hr = StrAllocString(&pControl->sczNote, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to command link control."); + + // Unconditional note entries aren't stored in the conditional notes list. + --pControl->cConditionalNotes; + } + } + + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT StartBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + // kick off + pControl->dwData = 0; + OnBillboardTimer(pTheme, pTheme->hwndParent, dwControl); + + if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL)) + { + ThmExitWithLastError(hr, "Failed to start billboard."); + } + + hr = S_OK; + } + } + +LExit: + return hr; +} + + +static HRESULT StopBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + ThemeControlEnable(pTheme, dwControl, FALSE); + + if (::KillTimer(pTheme->hwndParent, pControl->wId)) + { + hr = S_OK; + } + } + } + + return hr; +} + +static HRESULT EnsureFontInstance( + __in THEME* pTheme, + __in THEME_FONT* pFont, + __out THEME_FONT_INSTANCE** ppFontInstance + ) +{ + HRESULT hr = S_OK; + THEME_FONT_INSTANCE* pFontInstance = NULL; + LOGFONTW lf = { }; + + for (DWORD i = 0; i < pFont->cFontInstances; ++i) + { + pFontInstance = pFont->rgFontInstances + i; + if (pTheme->nDpi == pFontInstance->nDpi) + { + *ppFontInstance = pFontInstance; + ExitFunction(); + } + } + + hr = MemEnsureArraySize(reinterpret_cast(&pFont->rgFontInstances), pFont->cFontInstances, sizeof(THEME_FONT_INSTANCE), GROW_FONT_INSTANCES); + ThmExitOnFailure(hr, "Failed to allocate memory for font instances."); + + pFontInstance = pFont->rgFontInstances + pFont->cFontInstances; + pFontInstance->nDpi = pTheme->nDpi; + + lf.lfHeight = DpiuScaleValue(pFont->lfHeight, pFontInstance->nDpi); + lf.lfWeight = pFont->lfWeight; + lf.lfUnderline = pFont->lfUnderline; + lf.lfQuality = pFont->lfQuality; + + hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), pFont->sczFaceName); + ThmExitOnFailure(hr, "Failed to copy font name to create font."); + + pFontInstance->hFont = ::CreateFontIndirectW(&lf); + ThmExitOnNull(pFontInstance->hFont, hr, E_OUTOFMEMORY, "Failed to create DPI specific font."); + + ++pFont->cFontInstances; + *ppFontInstance = pFontInstance; + +LExit: + return hr; +} + + +static HRESULT FindImageList( + __in THEME* pTheme, + __in_z LPCWSTR wzImageListName, + __out HIMAGELIST *phImageList + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pTheme->cImageLists; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1)) + { + *phImageList = pTheme->rgImageLists[i].hImageList; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +static HRESULT DrawButton( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + DWORD dwSourceWidth = pControl->nDefaultDpiWidth; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE); + // "clicked" gets priority + if (ODS_SELECTED & pdis->itemState) + { + nSourceY += pControl->nDefaultDpiHeight * 2; + } + // then hover + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) + { + nSourceY += pControl->nDefaultDpiHeight; + } + // then focused + else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState) + { + nSourceY += pControl->nDefaultDpiHeight * 3; + } + + ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + + DrawControlText(pTheme, pdis, pControl, TRUE, FALSE); + + return S_OK; +} + + +static HRESULT DrawHyperlink( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DrawControlText(pTheme, pdis, pControl, FALSE, TRUE); + return S_OK; +} + + +static void DrawControlText( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl, + __in BOOL fCentered, + __in BOOL fDrawFocusRect + ) +{ + HRESULT hr = S_OK; + WCHAR wzText[256] = { }; + DWORD cchText = 0; + THEME_FONT* pFont = NULL; + THEME_FONT_INSTANCE* pFontInstance = NULL; + HFONT hfPrev = NULL; + + if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText)))) + { + // nothing to do + return; + } + + if (ODS_SELECTED & pdis->itemState) + { + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId); + } + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) + { + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId); + } + else + { + pFont = pTheme->rgFonts + pControl->dwFontId; + } + + hr = EnsureFontInstance(pTheme, pFont, &pFontInstance); + if (SUCCEEDED(hr)) + { + hfPrev = SelectFont(pdis->hDC, pFontInstance->hFont); + } + + ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL); + + if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState)) + { + ::DrawFocusRect(pdis->hDC, &pdis->rcItem); + } + + if (hfPrev) + { + SelectFont(pdis->hDC, hfPrev); + } +} + + +static HRESULT DrawImage( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; + DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left; + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; + DWORD dwSourceWidth = pControl->nDefaultDpiWidth; + + BLENDFUNCTION bf = { }; + bf.BlendOp = AC_SRC_OVER; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + // Try to draw the image with transparency and if that fails (usually because the image has no + // alpha channel) then draw the image as is. + if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, bf)) + { + ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); + } + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + return S_OK; +} + + +static HRESULT DrawProgressBar( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DWORD dwProgressColor = HIWORD(pControl->dwData); + DWORD dwProgressPercentage = LOWORD(pControl->dwData); + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; + DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * dwSourceHeight); + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + // Draw the left side of the progress bar. + ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY); + + // Draw the filled side of the progress bar, if there is any. + if (0 < dwCenter) + { + ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwSourceHeight, SRCCOPY); + } + + // Draw the unfilled side of the progress bar, if there is any. + if (dwCenter < static_cast(pdis->rcItem.right - 2)) + { + ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwSourceHeight, SRCCOPY); + } + + // Draw the right side of the progress bar. + ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX + 3, nSourceY, 1, dwSourceHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + return S_OK; +} + + +static BOOL DrawHoverControl( + __in THEME* pTheme, + __in BOOL fHover + ) +{ + BOOL fChangedHover = FALSE; + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, pTheme->hwndHover)); + + // Only hyperlinks and owner-drawn buttons have hover states. + if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || + (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)))) + { + if (fHover) + { + pControl->dwData |= THEME_CONTROL_DATA_HOVER; + } + else + { + pControl->dwData &= ~THEME_CONTROL_DATA_HOVER; + } + + ::InvalidateRect(pControl->hWnd, NULL, FALSE); + fChangedHover = TRUE; + } + + return fChangedHover; +} + + +static void FreePage( + __in THEME_PAGE* pPage + ) +{ + if (pPage) + { + ReleaseStr(pPage->sczName); + + if (pPage->cSavedVariables) + { + for (DWORD i = 0; i < pPage->cSavedVariables; ++i) + { + ReleaseStr(pPage->rgSavedVariables[i].sczValue); + } + } + + ReleaseMem(pPage->rgSavedVariables); + } +} + + +static void FreeImageList( + __in THEME_IMAGELIST* pImageList + ) +{ + if (pImageList) + { + ReleaseStr(pImageList->sczName); + ImageList_Destroy(pImageList->hImageList); + } +} + +static void FreeControl( + __in THEME_CONTROL* pControl + ) +{ + if (pControl) + { + if (::IsWindow(pControl->hWnd)) + { + ::CloseWindow(pControl->hWnd); + pControl->hWnd = NULL; + } + + ReleaseStr(pControl->sczName); + ReleaseStr(pControl->sczText); + ReleaseStr(pControl->sczTooltip); + ReleaseStr(pControl->sczNote); + ReleaseStr(pControl->sczEnableCondition); + ReleaseStr(pControl->sczVisibleCondition); + ReleaseStr(pControl->sczValue); + ReleaseStr(pControl->sczVariable); + + if (pControl->hImage) + { + ::DeleteBitmap(pControl->hImage); + } + + for (DWORD i = 0; i < pControl->cControls; ++i) + { + FreeControl(pControl->rgControls + i); + } + + for (DWORD i = 0; i < pControl->cActions; ++i) + { + FreeAction(&(pControl->rgActions[i])); + } + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + FreeColumn(&(pControl->ptcColumns[i])); + } + + for (DWORD i = 0; i < pControl->cConditionalText; ++i) + { + FreeConditionalText(&(pControl->rgConditionalText[i])); + } + + for (DWORD i = 0; i < pControl->cConditionalNotes; ++i) + { + FreeConditionalText(&(pControl->rgConditionalNotes[i])); + } + + for (DWORD i = 0; i < pControl->cTabs; ++i) + { + FreeTab(&(pControl->pttTabs[i])); + } + + ReleaseMem(pControl->rgActions) + ReleaseMem(pControl->ptcColumns); + ReleaseMem(pControl->rgConditionalText); + ReleaseMem(pControl->rgConditionalNotes); + ReleaseMem(pControl->pttTabs); + } +} + + +static void FreeAction( + __in THEME_ACTION* pAction + ) +{ + switch (pAction->type) + { + case THEME_ACTION_TYPE_BROWSE_DIRECTORY: + ReleaseStr(pAction->BrowseDirectory.sczVariableName); + break; + case THEME_ACTION_TYPE_CHANGE_PAGE: + ReleaseStr(pAction->ChangePage.sczPageName); + break; + } + + ReleaseStr(pAction->sczCondition); +} + + +static void FreeColumn( + __in THEME_COLUMN* pColumn + ) +{ + ReleaseStr(pColumn->pszName); +} + + +static void FreeConditionalText( + __in THEME_CONDITIONAL_TEXT* pConditionalText + ) +{ + ReleaseStr(pConditionalText->sczCondition); + ReleaseStr(pConditionalText->sczText); +} + + +static void FreeTab( + __in THEME_TAB* pTab + ) +{ + ReleaseStr(pTab->pszName); +} + + +static void FreeFontInstance( + __in THEME_FONT_INSTANCE* pFontInstance + ) +{ + if (pFontInstance->hFont) + { + ::DeleteObject(pFontInstance->hFont); + pFontInstance->hFont = NULL; + } +} + + +static void FreeFont( + __in THEME_FONT* pFont + ) +{ + if (pFont) + { + if (pFont->hBackground) + { + ::DeleteObject(pFont->hBackground); + pFont->hBackground = NULL; + } + + if (pFont->hForeground) + { + ::DeleteObject(pFont->hForeground); + pFont->hForeground = NULL; + } + + for (DWORD i = 0; i < pFont->cFontInstances; ++i) + { + FreeFontInstance(&(pFont->rgFontInstances[i])); + } + + ReleaseMem(pFont->rgFontInstances); + ReleaseStr(pFont->sczFaceName); + } +} + + +static DWORD CALLBACK RichEditStreamFromFileHandleCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG* pcb + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = reinterpret_cast(dwCookie); + + if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast(pcb), NULL)) + { + ThmExitWithLastError(hr, "Failed to read file"); + } + +LExit: + return hr; +} + + +static DWORD CALLBACK RichEditStreamFromMemoryCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG* pcb + ) +{ + HRESULT hr = S_OK; + MEMBUFFER_FOR_RICHEDIT* pBuffer = reinterpret_cast(dwCookie); + DWORD cbCopy = 0; + + if (pBuffer->iData < pBuffer->cbData) + { + cbCopy = min(static_cast(cb), pBuffer->cbData - pBuffer->iData); + memcpy(pbBuff, pBuffer->rgbData + pBuffer->iData, cbCopy); + + pBuffer->iData += cbCopy; + Assert(pBuffer->iData <= pBuffer->cbData); + } + + *pcb = cbCopy; + return hr; +} + + +static void CALLBACK OnBillboardTimer( + __in THEME* pTheme, + __in HWND hwnd, + __in UINT_PTR idEvent + ) +{ + HWND hwndControl = ::GetDlgItem(hwnd, static_cast(idEvent)); + if (hwndControl) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hwndControl)); + AssertSz(pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages."); + + if (pControl) + { + if (pControl->dwData < pControl->cControls) + { + ThemeShowChild(pTheme, pControl, pControl->dwData); + } + else if (pControl->fBillboardLoops) + { + pControl->dwData = 0; + ThemeShowChild(pTheme, pControl, pControl->dwData); + } + else // no more looping + { + ::KillTimer(hwnd, idEvent); + } + + ++pControl->dwData; + } + } +} + +static void OnBrowseDirectory( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH] = { }; + BROWSEINFOW browseInfo = { }; + PIDLIST_ABSOLUTE pidl = NULL; + + browseInfo.hwndOwner = hWnd; + browseInfo.pszDisplayName = wzPath; + browseInfo.lpszTitle = pTheme->sczCaption; + browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; + pidl = ::SHBrowseForFolderW(&browseInfo); + if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) + { + // Since editbox changes aren't immediately saved off, we have to treat them differently. + THEME_CONTROL* pTargetControl = NULL; + + for (DWORD i = 0; i < pTheme->cControls; ++i) + { + THEME_CONTROL* pControl = pTheme->rgControls + i; + + if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1)) + { + pTargetControl = pControl; + break; + } + } + + if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality) + { + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); + ThmExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName); + } + else if (pTheme->pfnSetStringVariable) + { + hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, FALSE, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); + } + else if (pTargetControl) + { + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); + ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName); + } + + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); + } + +LExit: + if (pidl) + { + ::CoTaskMemFree(pidl); + } +} + +static BOOL OnButtonClicked( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + BOOL fHandled = FALSE; + + if (THEME_CONTROL_TYPE_BUTTON == pControl->type || THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + if (pControl->cActions) + { + fHandled = TRUE; + THEME_ACTION* pChosenAction = pControl->pDefaultAction; + + if (pTheme->pfnEvaluateCondition) + { + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. + // This is the current implementation and can change at any time. + for (DWORD j = 0; j < pControl->cActions; ++j) + { + THEME_ACTION* pAction = pControl->rgActions + j; + + if (pAction->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition); + + if (fCondition) + { + pChosenAction = pAction; + break; + } + } + } + } + + if (pChosenAction) + { + switch (pChosenAction->type) + { + case THEME_ACTION_TYPE_BROWSE_DIRECTORY: + OnBrowseDirectory(pTheme, hWnd, pChosenAction); + break; + + case THEME_ACTION_TYPE_CLOSE_WINDOW: + ::SendMessageW(hWnd, WM_CLOSE, 0, 0); + break; + + case THEME_ACTION_TYPE_CHANGE_PAGE: + DWORD dwPageId = 0; + LPCWSTR pPageNames = pChosenAction->ChangePage.sczPageName; + ThemeGetPageIds(pTheme, &pPageNames, &dwPageId, 1); + + if (!dwPageId) + { + ThmExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName); + } + + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT); + ThemeShowPage(pTheme, dwPageId, SW_SHOW); + break; + } + } + } + } + else if (!pControl->fDisableVariableFunctionality && (pTheme->pfnSetNumericVariable || pTheme->pfnSetStringVariable)) + { + BOOL fRefresh = FALSE; + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_CHECKBOX: + if (pTheme->pfnSetNumericVariable && pControl->sczName && *pControl->sczName) + { + BOOL fChecked = ThemeIsControlChecked(pTheme, pControl->wId); + pTheme->pfnSetNumericVariable(pControl->sczName, fChecked ? 1 : 0, pTheme->pvVariableContext); + fRefresh = TRUE; + } + break; + case THEME_CONTROL_TYPE_RADIOBUTTON: + if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId)) + { + pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, FALSE, pTheme->pvVariableContext); + fRefresh = TRUE; + } + break; + } + + if (fRefresh) + { + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); + fHandled = TRUE; + } + } + +LExit: + return fHandled; +} + +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + UINT nDpi = HIWORD(wParam); + RECT* pRect = reinterpret_cast(lParam); + BOOL fIgnored = pTheme->nDpi == nDpi; + + if (fIgnored) + { + ExitFunction(); + } + + + pTheme->fForceResize = !pTheme->fAutoResize; + ScaleThemeFromWindow(pTheme, nDpi, pRect->left, pRect->top); + +LExit: + return !fIgnored; +} + +static void OnNcCreate( + __in THEME* pTheme, + __in HWND hWnd, + __in LPARAM lParam + ) +{ + DPIU_WINDOW_CONTEXT windowContext = { }; + CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); + + pTheme->hwndParent = hWnd; + + DpiuGetWindowContext(pTheme->hwndParent, &windowContext); + + if (windowContext.nDpi != pTheme->nDpi) + { + ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y, pCreateStruct->style, NULL != pCreateStruct->hMenu, pCreateStruct->dwExStyle); + } +} + +static HRESULT OnRichEditEnLink( + __in LPARAM lParam, + __in HWND hWndRichEdit, + __in HWND hWnd + ) +{ + HRESULT hr = S_OK; + LPWSTR sczLink = NULL; + ENLINK* link = reinterpret_cast(lParam); + + switch (link->msg) + { + case WM_LBUTTONDOWN: + { + hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2); + ThmExitOnFailure(hr, "Failed to allocate string for link."); + + TEXTRANGEW tr; + tr.chrg.cpMin = link->chrg.cpMin; + tr.chrg.cpMax = link->chrg.cpMax; + tr.lpstrText = sczLink; + + if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast(&tr))) + { + hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); + ThmExitOnFailure(hr, "Failed to launch link: %ls", sczLink); + } + + break; + } + + case WM_SETCURSOR: + ::SetCursor(vhCursorHand); + break; + } + +LExit: + ReleaseStr(sczLink); + + return hr; +} + +static BOOL ControlIsType( + __in const THEME* pTheme, + __in DWORD dwControl, + __in const THEME_CONTROL_TYPE type + ) +{ + BOOL fIsType = FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + fIsType = (pControl && type == pControl->type); + } + + return fIsType; +} + +static const THEME_CONTROL* FindControlFromHWnd( + __in const THEME* pTheme, + __in HWND hWnd, + __in_opt const THEME_CONTROL* pParentControl + ) +{ + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + + GetControls(pTheme, pParentControl, cControls, rgControls); + + // As we can't use GWLP_USERDATA (SysLink controls on Windows XP uses it too)... + for (DWORD i = 0; i < cControls; ++i) + { + if (hWnd == rgControls[i].hWnd) + { + return rgControls + i; + } + else if (0 < rgControls[i].cControls) + { + const THEME_CONTROL* pChildControl = FindControlFromHWnd(pTheme, hWnd, rgControls + i); + if (pChildControl) + { + return pChildControl; + } + } + } + + return NULL; +} + +static void GetControlDimensions( + __in const THEME_CONTROL* pControl, + __in const RECT* prcParent, + __out int* piWidth, + __out int* piHeight, + __out int* piX, + __out int* piY + ) +{ + *piWidth = pControl->nWidth + (0 < pControl->nWidth ? 0 : prcParent->right - max(0, pControl->nX)); + *piHeight = pControl->nHeight + (0 < pControl->nHeight ? 0 : prcParent->bottom - max(0, pControl->nY)); + *piX = pControl->nX + (-1 < pControl->nX ? 0 : prcParent->right - *piWidth); + *piY = pControl->nY + (-1 < pControl->nY ? 0 : prcParent->bottom - *piHeight); +} + +static HRESULT SizeListViewColumns( + __inout THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + RECT rcParent = { }; + int cNumExpandingColumns = 0; + int iExtraAvailableSize; + + if (!::GetWindowRect(pControl->hWnd, &rcParent)) + { + ThmExitWithLastError(hr, "Failed to get window rect of listview control."); + } + + iExtraAvailableSize = rcParent.right - rcParent.left; + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + if (pControl->ptcColumns[i].fExpands) + { + ++cNumExpandingColumns; + } + + iExtraAvailableSize -= pControl->ptcColumns[i].nBaseWidth; + } + + // Leave room for a vertical scroll bar just in case. + iExtraAvailableSize -= ::GetSystemMetrics(SM_CXVSCROLL); + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + if (pControl->ptcColumns[i].fExpands) + { + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth + (iExtraAvailableSize / cNumExpandingColumns); + // In case there is any remainder, use it up the first chance we get. + pControl->ptcColumns[i].nWidth += iExtraAvailableSize % cNumExpandingColumns; + iExtraAvailableSize -= iExtraAvailableSize % cNumExpandingColumns; + } + else + { + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth; + } + } + +LExit: + return hr; +} + + +static HRESULT ShowControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId, + __out_opt HWND* phwndFocus + ) +{ + HRESULT hr = S_OK; + DWORD iPageControl = 0; + HWND hwndFocus = NULL; + LPWSTR sczFormatString = NULL; + LPWSTR sczText = NULL; + THEME_SAVEDVARIABLE* pSavedVariable = NULL; + BOOL fHide = SW_HIDE == nCmdShow; + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPageId); + + // Save the editbox value if necessary (other control types save their values immediately). + if (pTheme->pfnSetStringVariable && !pControl->fDisableVariableFunctionality && + fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) + { + hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText); + ThmExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); + + hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, FALSE, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); + } + + HWND hWnd = pControl->hWnd; + + if (fHide && pControl->wPageId) + { + ::ShowWindow(hWnd, SW_HIDE); + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + StopBillboard(pTheme, pControl->wId); + } + + ExitFunction(); + } + + BOOL fEnabled = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); + BOOL fVisible = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDDEN); + + if (!pControl->fDisableVariableFunctionality) + { + if (pTheme->pfnEvaluateCondition) + { + // If the control has a VisibleCondition, check if it's true. + if (pControl->sczVisibleCondition) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + } + + // If the control has an EnableCondition, check if it's true. + if (pControl->sczEnableCondition) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + } + } + + // Try to format each control's text based on context, except for editboxes since their text comes from the user. + if (pTheme->pfnFormatString && ((pControl->sczText && *pControl->sczText) || pControl->cConditionalText) && THEME_CONTROL_TYPE_EDITBOX != pControl->type) + { + LPWSTR wzText = pControl->sczText; + LPWSTR wzNote = pControl->sczNote; + + if (pTheme->pfnEvaluateCondition) + { + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. + // This is the current implementation and can change at any time. + for (DWORD j = 0; j < pControl->cConditionalText; ++j) + { + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + j; + wzText = pConditionalText->sczText; + + if (pConditionalText->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition); + + if (fCondition) + { + wzText = pConditionalText->sczText; + break; + } + } + } + + for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) + { + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + j; + wzNote = pConditionalNote->sczText; + + if (pConditionalNote->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition); + + if (fCondition) + { + wzNote = pConditionalNote->sczText; + break; + } + } + } + } + + if (wzText && *wzText) + { + hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to format string: %ls", wzText); + } + else + { + ReleaseNullStr(sczText); + } + + ThemeSetTextControl(pTheme, pControl->wId, sczText); + + if (wzNote && *wzNote) + { + hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to format note: %ls", wzNote); + } + else + { + ReleaseNullStr(sczText); + } + + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(sczText)); + } + + // If this is a named control, do variable magic. + if (pControl->sczName && *pControl->sczName) + { + // If this is a checkbox control, + // try to set its default state to the state of a matching named variable. + if (pTheme->pfnGetNumericVariable && THEME_CONTROL_TYPE_CHECKBOX == pControl->type) + { + LONGLONG llValue = 0; + hr = pTheme->pfnGetNumericVariable(pControl->sczName, &llValue, pTheme->pvVariableContext); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName); + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczName; + + if (SUCCEEDED(hr)) + { + hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + } + + ++iPageControl; + } + + ThemeSendControlMessage(pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); + } + + // If this is an editbox control, + // try to set its default state to the state of a matching named variable. + if (pTheme->pfnFormatString && THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + hr = StrAllocFormatted(&sczFormatString, L"[%ls]", pControl->sczName); + ThmExitOnFailure(hr, "Failed to create format string: '%ls'", pControl->sczName); + + hr = pTheme->pfnFormatString(sczFormatString, &sczText, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to format string: '%ls'", sczFormatString); + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczName; + + if (SUCCEEDED(hr)) + { + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + } + + ++iPageControl; + } + + ThemeSetTextControl(pTheme, pControl->wId, sczText); + } + } + + // If this is a radio button associated with a variable, + // try to set its default state to the state of the variable. + if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type && pControl->sczVariable && *pControl->sczVariable) + { + hr = pTheme->pfnGetStringVariable(pControl->sczVariable, &sczText, pTheme->pvVariableContext); + if (E_NOTFOUND == hr) + { + ReleaseNullStr(sczText); + } + else + { + ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable); + } + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczVariable; + + if (SUCCEEDED(hr)) + { + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable); + } + + ++iPageControl; + } + + hr = S_OK; + + Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || (sczText && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1))); + } + } + + if (!fVisible || (!fEnabled && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED))) + { + ::ShowWindow(hWnd, SW_HIDE); + } + else + { + ::EnableWindow(hWnd, !fHide && fEnabled); + + if (!hwndFocus && pControl->wPageId && (pControl->dwStyle & WS_TABSTOP)) + { + hwndFocus = hWnd; + } + + ::ShowWindow(hWnd, nCmdShow); + } + + if (0 < pControl->cControls) + { + ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId); + } + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId) + { + if (fEnabled) + { + StartBillboard(pTheme, pControl->wId); + } + else + { + StopBillboard(pTheme, pControl->wId); + } + } + + if (phwndFocus) + { + *phwndFocus = hwndFocus; + } + +LExit: + ReleaseStr(sczFormatString); + ReleaseStr(sczText); + + return hr; +} + +static HRESULT ShowControls( + __in THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId + ) +{ + HRESULT hr = S_OK; + HWND hwndFocus = NULL; + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + + GetControls(pTheme, pParentControl, cControls, rgControls); + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + + // Only look at non-page controls and the specified page's controls. + if (!pControl->wPageId || pControl->wPageId == dwPageId) + { + hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus); + ThmExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i); + } + } + + if (hwndFocus) + { + ::SetFocus(hwndFocus); + } + +LExit: + return hr; +} + + +static LRESULT CALLBACK ControlGroupDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + if (pTheme) + { + switch (uMsg) + { + case WM_DRAWITEM: + ThemeDrawControl(pTheme, reinterpret_cast(lParam)); + return TRUE; + + case WM_CTLCOLORBTN: __fallthrough; + case WM_CTLCOLORSTATIC: + { + HBRUSH hBrush = NULL; + if (ThemeSetControlColor(pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam), &hBrush)) + { + return reinterpret_cast(hBrush); + } + } + break; + + case WM_SETCURSOR: + if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast(wParam))) + { + return TRUE; + } + break; + + case WM_PAINT: + if (::GetUpdateRect(hWnd, NULL, FALSE)) + { + PAINTSTRUCT ps; + ::BeginPaint(hWnd, &ps); + if (hWnd == pTheme->hwndParent) + { + ThemeDrawBackground(pTheme, &ps); + } + ::EndPaint(hWnd, &ps); + } + return 0; + + case WM_TIMER: + OnBillboardTimer(pTheme, hWnd, wParam); + break; + + case WM_NOTIFY: + if (lParam) + { + LPNMHDR pnmhdr = reinterpret_cast(lParam); + switch (pnmhdr->code) + { + // Tab/Shift+Tab support for rich-edit control. + case EN_MSGFILTER: + { + MSGFILTER* msgFilter = reinterpret_cast(lParam); + if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam) + { + BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT); + HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift); + ::SetFocus(hwndFocus); + return 1; + } + break; + } + + // Hyperlink clicks from rich-edit control. + case EN_LINK: + return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd)); + + // Clicks on a hypertext/syslink control. + case NM_CLICK: __fallthrough; + case NM_RETURN: + if (ControlIsType(pTheme, static_cast(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT)) + { + PNMLINK pnmlink = reinterpret_cast(lParam); + LITEM litem = pnmlink->item; + ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); + return 1; + } + + return 0; + } + } + break; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + if (lParam) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam); + if (pControl && OnButtonClicked(pTheme, hWnd, pControl)) + { + return 0; + } + } + break; + } + break; + } + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + + +static LRESULT CALLBACK PanelWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + LRESULT lres = 0; + THEME* pTheme = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); + pTheme = reinterpret_cast(lpcs->lpCreateParams); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pTheme)); + } + break; + + case WM_NCDESTROY: + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lres; + + case WM_NCHITTEST: + return HTCLIENT; + break; + } + + return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); +} + +static LRESULT CALLBACK StaticOwnerDrawWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_UPDATEUISTATE: + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + default: + return (*vpfnStaticOwnerDrawBaseWndProc)(hWnd, uMsg, wParam, lParam); + } +} + +static HRESULT LoadControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ) +{ + HRESULT hr = S_OK; + RECT rcParent = { }; + LPWSTR sczText = NULL; + BOOL fStartNewGroup = FALSE; + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + HWND hwndParent = pParentControl ? pParentControl->hWnd : pTheme->hwndParent; + int w = 0; + int h = 0; + int x = 0; + int y = 0; + + GetControls(pTheme, pParentControl, cControls, rgControls); + ::GetClientRect(hwndParent, &rcParent); + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; + THEME_FONT_INSTANCE* pControlFontInstance = NULL; + LPCWSTR wzWindowClass = NULL; + DWORD dwWindowBits = WS_CHILD; + DWORD dwWindowExBits = 0; + + if (fStartNewGroup) + { + dwWindowBits |= WS_GROUP; + fStartNewGroup = FALSE; + } + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_BILLBOARD: + __fallthrough; + case THEME_CONTROL_TYPE_PANEL: + wzWindowClass = THEME_WC_PANEL; + dwWindowBits |= WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + dwWindowExBits |= WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT; +#ifdef DEBUG + StrAllocFormatted(&pControl->sczText, L"Panel '%ls', id: %d", pControl->sczName, pControl->wId); +#endif + break; + + case THEME_CONTROL_TYPE_CHECKBOX: + dwWindowBits |= BS_AUTOCHECKBOX | BS_MULTILINE; // checkboxes are basically buttons with an extra bit tossed in. + __fallthrough; + case THEME_CONTROL_TYPE_BUTTON: + wzWindowClass = WC_BUTTONW; + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + dwWindowBits |= BS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + break; + + case THEME_CONTROL_TYPE_COMBOBOX: + wzWindowClass = WC_COMBOBOXW; + dwWindowBits |= CBS_DROPDOWNLIST | CBS_HASSTRINGS; + break; + + case THEME_CONTROL_TYPE_COMMANDLINK: + wzWindowClass = WC_BUTTONW; + dwWindowBits |= BS_COMMANDLINK; + break; + + case THEME_CONTROL_TYPE_EDITBOX: + wzWindowClass = WC_EDITW; + dwWindowBits |= ES_LEFT | ES_AUTOHSCROLL; + dwWindowExBits = WS_EX_CLIENTEDGE; + break; + + case THEME_CONTROL_TYPE_HYPERLINK: // hyperlinks are basically just owner drawn buttons. + wzWindowClass = THEME_WC_HYPERLINK; + dwWindowBits |= BS_OWNERDRAW | BTNS_NOPREFIX; + break; + + case THEME_CONTROL_TYPE_HYPERTEXT: + wzWindowClass = WC_LINK; + dwWindowBits |= LWS_NOPREFIX; + break; + + case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps). + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + wzWindowClass = THEME_WC_STATICOWNERDRAW; + dwWindowBits |= SS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Invalid image or image list coordinates."); + } + break; + + case THEME_CONTROL_TYPE_LABEL: + wzWindowClass = WC_STATICW; + break; + + case THEME_CONTROL_TYPE_LISTVIEW: + // If thmutil is handling the image list for this listview, tell Windows not to free it when the control is destroyed. + if (pControl->rghImageList[0] || pControl->rghImageList[1] || pControl->rghImageList[2] || pControl->rghImageList[3]) + { + pControl->dwStyle |= LVS_SHAREIMAGELISTS; + } + wzWindowClass = WC_LISTVIEWW; + break; + + case THEME_CONTROL_TYPE_PROGRESSBAR: + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control. + dwWindowBits |= SS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + else + { + wzWindowClass = PROGRESS_CLASSW; + } + break; + + case THEME_CONTROL_TYPE_RADIOBUTTON: + dwWindowBits |= BS_AUTORADIOBUTTON | BS_MULTILINE; + wzWindowClass = WC_BUTTONW; + + if (pControl->fLastRadioButton) + { + fStartNewGroup = TRUE; + } + break; + + case THEME_CONTROL_TYPE_RICHEDIT: + if (!vhModuleMsftEdit && !vhModuleRichEd) + { + hr = LoadSystemLibrary(L"Msftedit.dll", &vhModuleMsftEdit); + if (FAILED(hr)) + { + hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); + ThmExitOnFailure(hr, "Failed to load Rich Edit control library."); + } + } + + wzWindowClass = vhModuleMsftEdit ? MSFTEDIT_CLASS : RICHEDIT_CLASSW; + dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY; + break; + + case THEME_CONTROL_TYPE_STATIC: + wzWindowClass = WC_STATICW; + dwWindowBits |= SS_ETCHEDHORZ; + break; + + case THEME_CONTROL_TYPE_TAB: + wzWindowClass = WC_TABCONTROLW; + break; + + case THEME_CONTROL_TYPE_TREEVIEW: + wzWindowClass = WC_TREEVIEWW; + break; + } + ThmExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type); + + // Default control ids to the theme id and its index in the control array, unless there + // is a specific id to assign to a named control. + WORD wControlId = MAKEWORD(i, pTheme->wId); + for (DWORD iAssignControl = 0; pControl->sczName && iAssignControl < cAssignControlIds; ++iAssignControl) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pControl->sczName, -1, rgAssignControlIds[iAssignControl].wzName, -1)) + { + wControlId = rgAssignControlIds[iAssignControl].wId; + break; + } + } + + pControl->wId = wControlId; + + GetControlDimensions(pControl, &rcParent, &w, &h, &x, &y); + + BOOL fVisible = pControl->dwStyle & WS_VISIBLE; + BOOL fDisabled = pControl->dwStyle & WS_DISABLED; + + // If the control is supposed to be initially visible and it has a VisibleCondition, check if it's true. + if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + + if (!fVisible) + { + pControl->dwStyle &= ~WS_VISIBLE; + } + } + + // Disable controls that aren't visible so their shortcut keys don't trigger. + if (!fVisible) + { + dwWindowBits |= WS_DISABLED; + fDisabled = TRUE; + } + + // If the control is supposed to be initially enabled and it has an EnableCondition, check if it's true. + if (!fDisabled && pControl->sczEnableCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) + { + BOOL fEnable = TRUE; + + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + + fDisabled = !fEnable; + dwWindowBits |= fDisabled ? WS_DISABLED : 0; + } + + // Honor the HideWhenDisabled option. + if ((pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) && fVisible && fDisabled) + { + fVisible = FALSE; + pControl->dwStyle &= ~WS_VISIBLE; + } + + pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pTheme); + ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); + + if (pControl->sczTooltip) + { + if (!pTheme->hwndTooltip) + { + 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); + } + + if (pTheme->hwndTooltip) + { + TOOLINFOW toolinfo = {}; + toolinfo.cbSize = sizeof(toolinfo); + toolinfo.hwnd = hwndParent; + toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolinfo.uId = reinterpret_cast(pControl->hWnd); + toolinfo.lpszText = pControl->sczTooltip; + ::SendMessageW(pTheme->hwndTooltip, TTM_ADDTOOLW, 0, reinterpret_cast(&toolinfo)); + } + } + + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + if (pControl->sczNote) + { + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(pControl->sczNote)); + } + + if (pControl->hImage) + { + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast(pControl->hImage)); + } + else if (pControl->hIcon) + { + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_ICON, reinterpret_cast(pControl->hIcon)); + } + } + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE) + { + hr = ::SHAutoComplete(pControl->hWnd, SHACF_FILESYS_ONLY); + } + } + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle); + + hr = SizeListViewColumns(pControl); + ThmExitOnFailure(hr, "Failed to get size of list view columns."); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + LVCOLUMNW lvc = { }; + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + lvc.cx = pControl->ptcColumns[j].nWidth; + lvc.iSubItem = j; + lvc.pszText = pControl->ptcColumns[j].pszName; + lvc.fmt = LVCFMT_LEFT; + lvc.cchTextMax = 4; + + if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc))) + { + ThmExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j); + } + + // Return value tells us the old image list, we don't care. + if (pControl->rghImageList[0]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_NORMAL), reinterpret_cast(pControl->rghImageList[0])); + } + else if (pControl->rghImageList[1]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_SMALL), reinterpret_cast(pControl->rghImageList[1])); + } + else if (pControl->rghImageList[2]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_STATE), reinterpret_cast(pControl->rghImageList[2])); + } + else if (pControl->rghImageList[3]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_GROUPHEADER), reinterpret_cast(pControl->rghImageList[3])); + } + } + } + else if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) + { + ::SendMessageW(pControl->hWnd, EM_AUTOURLDETECT, static_cast(TRUE), 0); + ::SendMessageW(pControl->hWnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_LINK); + } + else if (THEME_CONTROL_TYPE_TAB == pControl->type) + { + ULONG_PTR hbrBackground = 0; + if (THEME_INVALID_ID != pControl->dwFontId) + { + hbrBackground = reinterpret_cast(pTheme->rgFonts[pControl->dwFontId].hBackground); + } + else + { + hbrBackground = ::GetClassLongPtr(pTheme->hwndParent, GCLP_HBRBACKGROUND); + } + ::SetClassLongPtr(pControl->hWnd, GCLP_HBRBACKGROUND, hbrBackground); + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + TCITEMW tci = { }; + tci.mask = TCIF_TEXT | TCIF_IMAGE; + tci.iImage = -1; + tci.pszText = pControl->pttTabs[j].pszName; + + if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci))) + { + ThmExitWithLastError(hr, "Failed to insert tab %u into tab control.", j); + } + } + } + + if (pControlFont) + { + hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); + ThmExitOnFailure(hr, "Failed to get DPI specific font."); + + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFontInstance->hFont, FALSE); + } + + // Initialize the text on all "application" (non-page) controls, best effort only. + if (pTheme->pfnFormatString && !pControl->wPageId && pControl->sczText && *pControl->sczText) + { + HRESULT hrFormat = pTheme->pfnFormatString(pControl->sczText, &sczText, pTheme->pvVariableContext); + if (SUCCEEDED(hrFormat)) + { + ThemeSetTextControl(pTheme, pControl->wId, sczText); + } + } + + if (pControl->cControls) + { + hr = LoadControls(pTheme, pControl, rgAssignControlIds, cAssignControlIds); + ThmExitOnFailure(hr, "Failed to load child controls."); + } + } + +LExit: + ReleaseStr(sczText); + + return hr; +} + +static HRESULT LocalizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + hr = LocalizeControl(pControl, pWixLoc); + ThmExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName); + } + +LExit: + return hr; +} + +static HRESULT LocalizeControl( + __in THEME_CONTROL* pControl, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + LPWSTR sczLocStringId = NULL; + + if (pControl->sczText && *pControl->sczText) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczText); + ThmExitOnFailure(hr, "Failed to localize control text."); + } + else if (pControl->sczName) + { + LOC_STRING* plocString = NULL; + + hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName); + ThmExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName); + + hr = LocGetString(pWixLoc, sczLocStringId, &plocString); + if (E_NOTFOUND != hr) + { + ThmExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName); + + hr = StrAllocString(&pControl->sczText, plocString->wzText, 0); + ThmExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText); + } + } + + if (pControl->sczTooltip && *pControl->sczTooltip) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip); + ThmExitOnFailure(hr, "Failed to localize control tooltip text."); + } + + if (pControl->sczNote && *pControl->sczNote) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczNote); + ThmExitOnFailure(hr, "Failed to localize control note text."); + } + + for (DWORD j = 0; j < pControl->cConditionalText; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText); + ThmExitOnFailure(hr, "Failed to localize conditional text."); + } + + for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText); + ThmExitOnFailure(hr, "Failed to localize conditional note."); + } + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName); + ThmExitOnFailure(hr, "Failed to localize column text."); + } + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName); + ThmExitOnFailure(hr, "Failed to localize tab text."); + } + + // Localize control's size, location, and text. + if (pControl->sczName) + { + hr = LocGetControl(pWixLoc, pControl->sczName, &pLocControl); + if (E_NOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ThmExitOnFailure(hr, "Failed to localize control."); + + if (LOC_CONTROL_NOT_SET != pLocControl->nX) + { + pControl->nDefaultDpiX = pLocControl->nX; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nY) + { + pControl->nDefaultDpiY = pLocControl->nY; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nWidth) + { + pControl->nDefaultDpiWidth = pLocControl->nWidth; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nHeight) + { + pControl->nDefaultDpiHeight = pLocControl->nHeight; + } + + if (pLocControl->wzText && *pLocControl->wzText) + { + hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0); + ThmExitOnFailure(hr, "Failed to localize control text."); + } + } + + hr = LocalizeControls(pControl->cControls, pControl->rgControls, pWixLoc); + +LExit: + ReleaseStr(sczLocStringId); + + return hr; +} + +static HRESULT LoadControlsString( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + hr = LoadControlString(pControl, hResModule); + ThmExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName); + } + +LExit: + return hr; +} + +static HRESULT LoadControlString( + __in THEME_CONTROL* pControl, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + if (UINT_MAX != pControl->uStringId) + { + hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText); + ThmExitOnFailure(hr, "Failed to load control text."); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + if (UINT_MAX != pControl->ptcColumns[j].uStringId) + { + hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName); + ThmExitOnFailure(hr, "Failed to load column text."); + } + } + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + if (UINT_MAX != pControl->pttTabs[j].uStringId) + { + hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName); + ThmExitOnFailure(hr, "Failed to load tab text."); + } + } + } + + hr = LoadControlsString(pControl->cControls, pControl->rgControls, hResModule); + +LExit: + return hr; +} + +static void ResizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const RECT* prcParent + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + ResizeControl(pControl, prcParent); + } +} + +static void ResizeControl( + __in THEME_CONTROL* pControl, + __in const RECT* prcParent + ) +{ + int w = 0; + int h = 0; + int x = 0; + int y = 0; + RECT rcControl = { }; + + GetControlDimensions(pControl, prcParent, &w, &h, &x, &y); + ::SetWindowPos(pControl->hWnd, NULL, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER); + +#ifdef DEBUG + if (THEME_CONTROL_TYPE_BUTTON == pControl->type) + { + Trace(REPORT_STANDARD, "Resizing button (%ls/%ls) to (%d,%d)+(%d,%d) for parent (%d,%d)-(%d,%d)", + pControl->sczName, pControl->sczText, x, y, w, h, prcParent->left, prcParent->top, prcParent->right, prcParent->bottom); + } +#endif + + if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + SizeListViewColumns(pControl); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + if (-1 == ::SendMessageW(pControl->hWnd, LVM_SETCOLUMNWIDTH, (WPARAM) (int) (j), (LPARAM) (pControl->ptcColumns[j].nWidth))) + { + Trace(REPORT_DEBUG, "Failed to resize listview column %u with error %u", j, ::GetLastError()); + return; + } + } + } + + if (pControl->cControls) + { + ::GetClientRect(pControl->hWnd, &rcControl); + ResizeControls(pControl->cControls, pControl->rgControls, &rcControl); + } +} + +static void ScaleThemeFromWindow( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ) +{ + DWORD dwStyle = GetWindowStyle(pTheme->hwndParent); + BOOL fMenu = NULL != ::GetMenu(pTheme->hwndParent); + DWORD dwExStyle = GetWindowExStyle(pTheme->hwndParent); + + ScaleTheme(pTheme, nDpi, x, y, dwStyle, fMenu, dwExStyle); +} + +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle + ) +{ + RECT rect = { }; + + pTheme->nDpi = nDpi; + + pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi); + pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi); + pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); + pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); + + rect.left = x; + rect.top = y; + rect.right = x + pTheme->nWidth; + rect.bottom = y + pTheme->nHeight; + DpiuAdjustWindowRect(&rect, dwStyle, fMenu, dwExStyle, pTheme->nDpi); + pTheme->nWindowWidth = rect.right - rect.left; + pTheme->nWindowHeight = rect.bottom - rect.top; + + ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi); + + if (pTheme->hwndParent) + { + ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, SWP_NOACTIVATE | SWP_NOZORDER); + } +} + +static void ScaleControls( + __in THEME* pTheme, + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in UINT nDpi + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + ScaleControl(pTheme, pControl, nDpi); + } +} + +static void ScaleControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in UINT nDpi + ) +{ + HRESULT hr = S_OK; + THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; + THEME_FONT_INSTANCE* pControlFontInstance = NULL; + + if (pControlFont) + { + hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); + if (SUCCEEDED(hr) && pControl->hWnd) + { + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM)pControlFontInstance->hFont, FALSE); + } + } + + if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + THEME_COLUMN* pColumn = pControl->ptcColumns + i; + + pColumn->nBaseWidth = DpiuScaleValue(pColumn->nDefaultDpiBaseWidth, nDpi); + } + } + + pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi); + pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi); + pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi); + pControl->nY = DpiuScaleValue(pControl->nDefaultDpiY, nDpi); + + if (pControl->cControls) + { + ScaleControls(pTheme, pControl->cControls, pControl->rgControls, nDpi); + } +} + +static void UnloadControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + pControl->hWnd = NULL; + + UnloadControls(pControl->cControls, pControl->rgControls); + } +} + 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define TimeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) +#define TimeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) +#define TimeExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) +#define TimeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) +#define TimeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_TIMEUTIL, e, x, s, __VA_ARGS__) +#define TimeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_TIMEUTIL, g, x, s, __VA_ARGS__) + +const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; +const 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" }; +enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone }; +enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone }; + +// prototypes +static HRESULT DayFromString( + __in_z LPCWSTR wzDay, + __out WORD* pwDayOfWeek + ); +static HRESULT MonthFromString( + __in_z LPCWSTR wzMonth, + __out WORD* pwMonthOfYear + ); + + +/******************************************************************** + TimeFromString - converts string to FILETIME + +*******************************************************************/ +extern "C" HRESULT DAPI TimeFromString( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ) +{ + Assert(wzTime && pFileTime); + + HRESULT hr = S_OK; + LPWSTR pwzTime = NULL; + + SYSTEMTIME sysTime = { }; + TIME_PARSER timeParser = DayOfWeek; + + LPCWSTR pwzStart = NULL; + LPWSTR pwzEnd = NULL; + + hr = StrAllocString(&pwzTime, wzTime, 0); + TimeExitOnFailure(hr, "Failed to copy time."); + + pwzStart = pwzEnd = pwzTime; + while (pwzEnd && *pwzEnd) + { + if (L',' == *pwzEnd || L' ' == *pwzEnd || L':' == *pwzEnd) + { + *pwzEnd = L'\0'; // null terminate + ++pwzEnd; + + while (L' ' == *pwzEnd) + { + ++pwzEnd; // and skip past the blank space + } + + switch (timeParser) + { + case DayOfWeek: + hr = DayFromString(pwzStart, &sysTime.wDayOfWeek); + TimeExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart); + break; + + case DayOfMonth: + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case MonthOfYear: + hr = MonthFromString(pwzStart, &sysTime.wMonth); + TimeExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart); + break; + + case Year: + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Hours: + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Minutes: + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Seconds: + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case TimeZone: + // TODO: do something with this in the future, but this should only hit outside of the while loop. + break; + + default: + break; + } + + pwzStart = pwzEnd; + timeParser = (TIME_PARSER)((int)timeParser + 1); + } + + ++pwzEnd; + } + + + if (!::SystemTimeToFileTime(&sysTime, pFileTime)) + { + TimeExitWithLastError(hr, "Failed to convert system time to file time."); + } + +LExit: + ReleaseStr(pwzTime); + + return hr; +} + +/******************************************************************** + TimeFromString3339 - converts string formated in accorance with RFC3339 to FILETIME + http://tools.ietf.org/html/rfc3339 +*******************************************************************/ +extern "C" HRESULT DAPI TimeFromString3339( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ) +{ + Assert(wzTime && pFileTime); + + HRESULT hr = S_OK; + LPWSTR pwzTime = NULL; + + SYSTEMTIME sysTime = { }; + TIME_PARSERRFC3339 timeParser = RFC3339_Year; + + LPCWSTR pwzStart = NULL; + LPWSTR pwzEnd = NULL; + + hr = StrAllocString(&pwzTime, wzTime, 0); + TimeExitOnFailure(hr, "Failed to copy time."); + + pwzStart = pwzEnd = pwzTime; + while (pwzEnd && *pwzEnd) + { + if (L'T' == *pwzEnd || L':' == *pwzEnd || L'-' == *pwzEnd) + { + *pwzEnd = L'\0'; // null terminate + ++pwzEnd; + + switch (timeParser) + { + case RFC3339_Year: + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Month: + sysTime.wMonth = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Day: + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Hours: + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Minutes: + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Seconds: + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_TimeZone: + // TODO: do something with this in the future, but this should only hit outside of the while loop. + break; + + default: + break; + } + + pwzStart = pwzEnd; + timeParser = (TIME_PARSERRFC3339)((int)timeParser + 1); + } + + ++pwzEnd; + } + + + if (!::SystemTimeToFileTime(&sysTime, pFileTime)) + { + TimeExitWithLastError(hr, "Failed to convert system time to file time."); + } + +LExit: + ReleaseStr(pwzTime); + + return hr; +} +/**************************************************************************** +TimeCurrentTime - gets the current time in string format + +****************************************************************************/ +extern "C" HRESULT DAPI TimeCurrentTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ) +{ + SYSTEMTIME st; + + if (fGMT) + { + ::GetSystemTime(&st); + } + else + { + SYSTEMTIME stGMT; + TIME_ZONE_INFORMATION tzi; + + ::GetTimeZoneInformation(&tzi); + ::GetSystemTime(&stGMT); + ::SystemTimeToTzSpecificLocalTime(&tzi, &stGMT, &st); + } + + return StrAllocFormatted(ppwz, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); +} + + +/**************************************************************************** +TimeCurrentDateTime - gets the current date and time in string format, + per format described in RFC 3339 +****************************************************************************/ +extern "C" HRESULT DAPI TimeCurrentDateTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ) +{ + SYSTEMTIME st; + + ::GetSystemTime(&st); + + return TimeSystemDateTime(ppwz, &st, fGMT); +} + + +/**************************************************************************** +TimeSystemDateTime - converts the provided system time struct to string format, + per format described in RFC 3339 +****************************************************************************/ +extern "C" HRESULT DAPI TimeSystemDateTime( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in BOOL fGMT + ) +{ + DWORD dwAbsBias = 0; + + if (fGMT) + { + return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02huZ", pst->wYear, pst->wMonth, pst->wDay, pst->wHour, pst->wMinute, pst->wSecond); + } + else + { + SYSTEMTIME st; + TIME_ZONE_INFORMATION tzi; + + ::GetTimeZoneInformation(&tzi); + ::SystemTimeToTzSpecificLocalTime(&tzi, pst, &st); + dwAbsBias = abs(tzi.Bias); + + 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); + } +} + + +/**************************************************************************** +TimeSystemToDateTimeString - converts the provided system time struct to + string format representing date and time for the specified locale +****************************************************************************/ +HRESULT DAPI TimeSystemToDateTimeString( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME* pst, + __in LCID locale + ) +{ + HRESULT hr = S_OK; + const WCHAR * DATE_FORMAT = L"MMM dd',' yyyy',' "; + const WCHAR * TIME_FORMAT = L"hh':'mm':'ss tt"; + int iLenDate = 0; + int iLenTime = 0; + + iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0); + if (0 >= iLenDate) + { + TimeExitWithLastError(hr, "Failed to get date format with NULL"); + } + + iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0); + if (0 >= iLenTime) + { + TimeExitWithLastError(hr, "Failed to get time format with NULL"); + } + + // Between both lengths we account for 2 null terminators, and only need one, so we subtract one + hr = StrAlloc(ppwz, iLenDate + iLenTime - 1); + TimeExitOnFailure(hr, "Failed to allocate string"); + + if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate)) + { + TimeExitWithLastError(hr, "Failed to get date format with buffer"); + } + // Space to separate them + (*ppwz)[iLenDate - 1] = ' '; + + if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime)) + { + TimeExitWithLastError(hr, "Failed to get time format with buffer"); + } + +LExit: + return hr; +} + +/******************************************************************** + DayFromString - converts string to day + +*******************************************************************/ +static HRESULT DayFromString( + __in_z LPCWSTR wzDay, + __out WORD* pwDayOfWeek + ) +{ + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name + + for (WORD i = 0; i < countof(DAY_OF_WEEK); ++i) + { + if (0 == lstrcmpW(wzDay, DAY_OF_WEEK[i])) + { + *pwDayOfWeek = i; + hr = S_OK; + break; + } + } + + return hr; +} + + +/******************************************************************** + MonthFromString - converts string to month + +*******************************************************************/ +static HRESULT MonthFromString( + __in_z LPCWSTR wzMonth, + __out WORD* pwMonthOfYear + ) +{ + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name + + for (WORD i = 0; i < countof(MONTH_OF_YEAR); ++i) + { + if (0 == lstrcmpW(wzMonth, MONTH_OF_YEAR[i])) + { + *pwMonthOfYear = i; + hr = S_OK; + break; + } + } + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define UncExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) +#define UncExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) +#define UncExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) +#define UncExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) +#define UncExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_UNCUTIL, e, x, s, __VA_ARGS__) +#define UncExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_UNCUTIL, g, x, s, __VA_ARGS__) + +DAPI_(HRESULT) UncConvertFromMountedDrive( + __inout LPWSTR *psczUNCPath, + __in LPCWSTR sczMountedDrivePath + ) +{ + HRESULT hr = S_OK; + DWORD dwLength = 0; + DWORD er = ERROR_SUCCESS; + LPWSTR sczDrive = NULL; + + // Only copy drive letter and colon + hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2); + UncExitOnFailure(hr, "Failed to copy drive"); + + // ERROR_NOT_CONNECTED means it's not a mapped drive + er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength); + if (ERROR_MORE_DATA == er) + { + er = ERROR_SUCCESS; + + hr = StrAlloc(psczUNCPath, dwLength); + UncExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength); + + er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength); + if (ERROR_CONNECTION_UNAVAIL == er) + { + // 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 + er = ERROR_SUCCESS; + } + UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive); + + // Skip drive letter and colon + hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0); + UncExitOnFailure(hr, "Failed to copy rest of database path"); + } + else + { + if (ERROR_SUCCESS == er) + { + er = ERROR_NO_DATA; + } + + UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive); + } + +LExit: + ReleaseStr(sczDrive); + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define UriExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) +#define UriExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) +#define UriExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) +#define UriExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) +#define UriExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_URIUTIL, e, x, s, __VA_ARGS__) +#define UriExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_URIUTIL, g, x, s, __VA_ARGS__) + + +// +// UriCanonicalize - canonicalizes a URI. +// +extern "C" HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ) +{ + HRESULT hr = S_OK; + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; + DWORD cch = countof(wz); + + if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE)) + { + UriExitWithLastError(hr, "Failed to canonicalize URI."); + } + + hr = StrAllocString(psczUri, wz, cch); + UriExitOnFailure(hr, "Failed copy canonicalized URI."); + +LExit: + return hr; +} + + +// +// UriCrack - cracks a URI into constituent parts. +// +extern "C" HRESULT DAPI UriCrack( + __in_z LPCWSTR wzUri, + __out_opt INTERNET_SCHEME* pScheme, + __deref_opt_out_z LPWSTR* psczHostName, + __out_opt INTERNET_PORT* pPort, + __deref_opt_out_z LPWSTR* psczUser, + __deref_opt_out_z LPWSTR* psczPassword, + __deref_opt_out_z LPWSTR* psczPath, + __deref_opt_out_z LPWSTR* psczQueryString + ) +{ + HRESULT hr = S_OK; + URL_COMPONENTSW components = { }; + WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; + WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1]; + WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1]; + WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1]; + + components.dwStructSize = sizeof(URL_COMPONENTSW); + + if (psczHostName) + { + components.lpszHostName = wzHostName; + components.dwHostNameLength = countof(wzHostName); + } + + if (psczUser) + { + components.lpszUserName = wzUserName; + components.dwUserNameLength = countof(wzUserName); + } + + if (psczPassword) + { + components.lpszPassword = wzPassword; + components.dwPasswordLength = countof(wzPassword); + } + + if (psczPath) + { + components.lpszUrlPath = wzPath; + components.dwUrlPathLength = countof(wzPath); + } + + if (psczQueryString) + { + components.lpszExtraInfo = wzQueryString; + components.dwExtraInfoLength = countof(wzQueryString); + } + + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components)) + { + UriExitWithLastError(hr, "Failed to crack URI."); + } + + if (pScheme) + { + *pScheme = components.nScheme; + } + + if (psczHostName) + { + hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength); + UriExitOnFailure(hr, "Failed to copy host name."); + } + + if (pPort) + { + *pPort = components.nPort; + } + + if (psczUser) + { + hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength); + UriExitOnFailure(hr, "Failed to copy user name."); + } + + if (psczPassword) + { + hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength); + UriExitOnFailure(hr, "Failed to copy password."); + } + + if (psczPath) + { + hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength); + UriExitOnFailure(hr, "Failed to copy path."); + } + + if (psczQueryString) + { + hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength); + UriExitOnFailure(hr, "Failed to copy query string."); + } + +LExit: + return hr; +} + + +// +// UriCrackEx - cracks a URI into URI_INFO. +// +extern "C" HRESULT DAPI UriCrackEx( + __in_z LPCWSTR wzUri, + __in URI_INFO* pUriInfo + ) +{ + HRESULT hr = S_OK; + + hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString); + UriExitOnFailure(hr, "Failed to crack URI."); + +LExit: + return hr; +} + + +// +// UriInfoUninitialize - frees the memory in a URI_INFO struct. +// +extern "C" void DAPI UriInfoUninitialize( + __in URI_INFO* pUriInfo + ) +{ + ReleaseStr(pUriInfo->sczHostName); + ReleaseStr(pUriInfo->sczUser); + ReleaseStr(pUriInfo->sczPassword); + ReleaseStr(pUriInfo->sczPath); + ReleaseStr(pUriInfo->sczQueryString); + memset(pUriInfo, 0, sizeof(URI_INFO)); +} + + +// +// UriCreate - creates a URI from constituent parts. +// +extern "C" HRESULT DAPI UriCreate( + __inout_z LPWSTR* psczUri, + __in INTERNET_SCHEME scheme, + __in_z_opt LPWSTR wzHostName, + __in INTERNET_PORT port, + __in_z_opt LPWSTR wzUser, + __in_z_opt LPWSTR wzPassword, + __in_z_opt LPWSTR wzPath, + __in_z_opt LPWSTR wzQueryString + ) +{ + HRESULT hr = S_OK; + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; + DWORD cch = countof(wz); + URL_COMPONENTSW components = { }; + + components.dwStructSize = sizeof(URL_COMPONENTSW); + components.nScheme = scheme; + components.lpszHostName = wzHostName; + components.nPort = port; + components.lpszUserName = wzUser; + components.lpszPassword = wzPassword; + components.lpszUrlPath = wzPath; + components.lpszExtraInfo = wzQueryString; + + if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch)) + { + UriExitWithLastError(hr, "Failed to create URI."); + } + + hr = StrAllocString(psczUri, wz, cch); + UriExitOnFailure(hr, "Failed copy created URI."); + +LExit: + return hr; +} + + +// +// UriGetServerAndResource - gets the server and resource as independent strings from a URI. +// +// NOTE: This function is useful for the InternetConnect/HttpRequest APIs. +// +extern "C" HRESULT DAPI UriGetServerAndResource( + __in_z LPCWSTR wzUri, + __out_z LPWSTR* psczServer, + __out_z LPWSTR* psczResource + ) +{ + HRESULT hr = S_OK; + INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN; + LPWSTR sczHostName = NULL; + INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER; + LPWSTR sczUser = NULL; + LPWSTR sczPassword = NULL; + LPWSTR sczPath = NULL; + LPWSTR sczQueryString = NULL; + + hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString); + UriExitOnFailure(hr, "Failed to crack URI."); + + hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL); + UriExitOnFailure(hr, "Failed to allocate server URI."); + + hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString); + UriExitOnFailure(hr, "Failed to allocate resource URI."); + +LExit: + ReleaseStr(sczQueryString); + ReleaseStr(sczPath); + ReleaseStr(sczPassword); + ReleaseStr(sczUser); + ReleaseStr(sczHostName); + + return hr; +} + + +// +// UriFile - returns the file part of the URI. +// +extern "C" HRESULT DAPI UriFile( + __deref_out_z LPWSTR* psczFile, + __in_z LPCWSTR wzUri + ) +{ + HRESULT hr = S_OK; + WCHAR wz[MAX_PATH + 1]; + DWORD cch = countof(wz); + URL_COMPONENTSW uc = { }; + + uc.dwStructSize = sizeof(uc); + uc.lpszUrlPath = wz; + uc.dwUrlPathLength = cch; + + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc)) + { + UriExitWithLastError(hr, "Failed to crack URI."); + } + + // Copy only the file name. Fortunately, PathFile() understands that + // forward slashes can be directory separators like backslashes. + hr = StrAllocString(psczFile, PathFile(wz), 0); + UriExitOnFailure(hr, "Failed to copy file name"); + +LExit: + return hr; +} + + +/******************************************************************* + UriProtocol - determines the protocol of a URI. + +********************************************************************/ +extern "C" HRESULT DAPI UriProtocol( + __in_z LPCWSTR wzUri, + __out URI_PROTOCOL* pProtocol + ) +{ + Assert(wzUri && *wzUri); + Assert(pProtocol); + + HRESULT hr = S_OK; + + if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L't' == wzUri[2] || L'T' == wzUri[2]) && + (L'p' == wzUri[3] || L'P' == wzUri[3]) && + (L's' == wzUri[4] || L'S' == wzUri[4]) && + L':' == wzUri[5] && + L'/' == wzUri[6] && + L'/' == wzUri[7]) + { + *pProtocol = URI_PROTOCOL_HTTPS; + } + else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L't' == wzUri[2] || L'T' == wzUri[2]) && + (L'p' == wzUri[3] || L'P' == wzUri[3]) && + L':' == wzUri[4] && + L'/' == wzUri[5] && + L'/' == wzUri[6]) + { + *pProtocol = URI_PROTOCOL_HTTP; + } + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L'p' == wzUri[2] || L'P' == wzUri[2]) && + L':' == wzUri[3] && + L'/' == wzUri[4] && + L'/' == wzUri[5]) + { + *pProtocol = URI_PROTOCOL_FTP; + } + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && + (L'i' == wzUri[1] || L'I' == wzUri[1]) && + (L'l' == wzUri[2] || L'L' == wzUri[2]) && + (L'e' == wzUri[3] || L'E' == wzUri[3]) && + L':' == wzUri[4] && + L'/' == wzUri[5] && + L'/' == wzUri[6]) + { + *pProtocol = URI_PROTOCOL_FILE; + } + else + { + *pProtocol = URI_PROTOCOL_UNKNOWN; + } + + return hr; +} + + +/******************************************************************* + UriRoot - returns the root of the path specified in the URI. + + examples: + file:///C:\path\path -> C:\ + file://server/share/path/path -> \\server\share + http://www.example.com/path/path -> http://www.example.com/ + ftp://ftp.example.com/path/path -> ftp://www.example.com/ + + NOTE: This function should only be used on cannonicalized URIs. + It does not cannonicalize itself. +********************************************************************/ +extern "C" HRESULT DAPI UriRoot( + __in_z LPCWSTR wzUri, + __out LPWSTR* ppwzRoot, + __out_opt URI_PROTOCOL* pProtocol + ) +{ + Assert(wzUri && *wzUri); + Assert(ppwzRoot); + + HRESULT hr = S_OK; + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; + LPCWSTR pwcSlash = NULL; + + hr = UriProtocol(wzUri, &protocol); + UriExitOnFailure(hr, "Invalid URI."); + + switch (protocol) + { + case URI_PROTOCOL_FILE: + if (L'/' == wzUri[7]) // file path + { + if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9]) + { + hr = StrAlloc(ppwzRoot, 4); + UriExitOnFailure(hr, "Failed to allocate string for root of URI."); + *ppwzRoot[0] = wzUri[8]; + *ppwzRoot[1] = L':'; + *ppwzRoot[2] = L'\\'; + *ppwzRoot[3] = L'\0'; + } + else + { + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Invalid file path in URI."); + } + } + else // UNC share + { + pwcSlash = wcschr(wzUri + 8, L'/'); + if (!pwcSlash) + { + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Invalid server name in URI."); + } + else + { + hr = StrAllocString(ppwzRoot, L"\\\\", 64); + UriExitOnFailure(hr, "Failed to allocate string for root of URI."); + + pwcSlash = wcschr(pwcSlash + 1, L'/'); + if (pwcSlash) + { + hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8); + UriExitOnFailure(hr, "Failed to add server/share to root of URI."); + } + else + { + hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0); + UriExitOnFailure(hr, "Failed to add server/share to root of URI."); + } + + // replace all slashes with backslashes to be truly UNC. + for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc) + { + if (L'/' == *pwc) + { + *pwc = L'\\'; + } + } + } + } + break; + + case URI_PROTOCOL_FTP: + pwcSlash = wcschr(wzUri + 6, L'/'); + if (pwcSlash) + { + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + else + { + hr = StrAllocString(ppwzRoot, wzUri, 0); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + break; + + case URI_PROTOCOL_HTTP: + pwcSlash = wcschr(wzUri + 7, L'/'); + if (pwcSlash) + { + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + else + { + hr = StrAllocString(ppwzRoot, wzUri, 0); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + break; + + default: + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Unknown URI protocol."); + } + + if (pProtocol) + { + *pProtocol = protocol; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI UriResolve( + __in_z LPCWSTR wzUri, + __in_opt LPCWSTR wzBaseUri, + __out LPWSTR* ppwzResolvedUri, + __out_opt URI_PROTOCOL* pResolvedProtocol + ) +{ + UNREFERENCED_PARAMETER(wzUri); + UNREFERENCED_PARAMETER(wzBaseUri); + UNREFERENCED_PARAMETER(ppwzResolvedUri); + UNREFERENCED_PARAMETER(pResolvedProtocol); + + HRESULT hr = E_NOTIMPL; +#if 0 + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; + + hr = UriProtocol(wzUri, &protocol); + UriExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); + + UriExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); + + if (URI_PROTOCOL_UNKNOWN == protocol) + { + UriExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); + + if (L'/' == *wzUri || L'\\' == *wzUri) + { + hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol); + UriExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); + + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); + UriExitOnFailure(hr, "Failed to concat file to base URI."); + } + else + { + hr = UriProtocol(wzBaseUri, &protocol); + UriExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); + + LPCWSTR pwcFile = const_cast (UriFile(wzBaseUri)); + if (!pwcFile) + { + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); + } + + hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri); + UriExitOnFailure(hr, "Failed to allocate string for resolved URI."); + + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); + UriExitOnFailure(hr, "Failed to concat file to resolved URI."); + } + } + else + { + hr = StrAllocString(ppwzResolvedUri, wzUri, 0); + UriExitOnFailure(hr, "Failed to copy resolved URI."); + } + + if (pResolvedProtocol) + { + *pResolvedProtocol = protocol; + } + +LExit: +#endif + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// UserExit macros +#define UserExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) +#define UserExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) +#define UserExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) +#define UserExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) +#define UserExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_USERUTIL, e, x, s, __VA_ARGS__) +#define UserExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_USERUTIL, g, x, s, __VA_ARGS__) + +static BOOL CheckIsMemberHelper( + __in_z LPCWSTR pwzGroupUserDomain, + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, + __in DWORD cguiGroupData + ); + +/******************************************************************* + UserBuildDomainUserName - builds a DOMAIN\USERNAME string + +********************************************************************/ +extern "C" HRESULT DAPI UserBuildDomainUserName( + __out_ecount_z(cchDest) LPWSTR wzDest, + __in int cchDest, + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain + ) +{ + HRESULT hr = S_OK; + DWORD cchLeft = cchDest; + WCHAR* pwz = wzDest; + DWORD cchWz = cchDest; + DWORD cch; + + cch = lstrlenW(pwzDomain); + if (cch >= cchLeft) + { + hr = ERROR_MORE_DATA; + UserExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain); + } + else if (cch > 0) + { + // handle the domain case + + hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0' + UserExitOnFailure(hr, "Failed to copy Domain onto string."); + + cchLeft -= cch; + pwz += cch; + cchWz -= cch; + + if (1 >= cchLeft) + { + hr = ERROR_MORE_DATA; + UserExitOnFailure(hr, "Insufficient buffer size while building domain user name"); + } + + hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0' + UserExitOnFailure(hr, "Failed to copy backslash onto string."); + + --cchLeft; + ++pwz; + --cchWz; + } + + cch = lstrlenW(pwzName); + if (cch >= cchLeft) + { + hr = ERROR_MORE_DATA; + UserExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName); + } + + hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0' + UserExitOnFailure(hr, "Failed to copy User name onto string."); + +LExit: + return hr; +} + + +/******************************************************************* + Checks whether a user is a member of a group - outputs the result via lpfMember +********************************************************************/ +extern "C" HRESULT DAPI UserCheckIsMember( + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain, + __in_z LPCWSTR pwzGroupName, + __in_z LPCWSTR pwzGroupDomain, + __out LPBOOL lpfMember + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + DWORD dwRead = 0; + DWORD dwTotal = 0; + LPCWSTR wz = NULL; + GROUP_USERS_INFO_0 *pguiGroupData = NULL; + WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME + WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME + BSTR bstrUser = NULL; + BSTR bstrGroup = NULL; + + IADsGroup *pGroup = NULL; + VARIANT_BOOL vtBoolResult = VARIANT_FALSE; + + hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain); + UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + + hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain); + UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + + if (pwzDomain && *pwzDomain) + { + wz = pwzDomain; + } + + er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); + // Ignore these errors, and just go to the fallback checks + if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er) + { + 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)); + er = ERROR_SUCCESS; + } + UserExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName); + + if (dwRead != dwTotal) + { + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + UserExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName); + } + + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + if (NULL != pguiGroupData) + { + ::NetApiBufferFree(pguiGroupData); + pguiGroupData = NULL; + } + + // If we fail with the global groups, try again with the local groups + er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); + // Ignore these errors, and just go to the fallback checks + if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er) + { + 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)); + er = ERROR_SUCCESS; + } + UserExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName); + + if (dwRead != dwTotal) + { + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + UserExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName); + } + + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + // If the above methods failed, let's try active directory + hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser); + UserExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain); + + hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup); + UserExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain); + + if (lstrlenW(pwzGroupDomain) > 0) + { + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); + UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->IsMember(bstrUser, &vtBoolResult); + UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + } + + if (vtBoolResult) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); + UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->IsMember(bstrUser, &vtBoolResult); + UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + + if (vtBoolResult) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + +LExit: + ReleaseObject(pGroup); + ReleaseBSTR(bstrUser); + ReleaseBSTR(bstrGroup); + + if (NULL != pguiGroupData) + { + ::NetApiBufferFree(pguiGroupData); + } + + return hr; +} + + +/******************************************************************* + Takes a domain and name, and allocates a BSTR which represents + DOMAIN\NAME's active directory path. The BSTR this function returns + should be released manually using the ReleaseBSTR() macro. +********************************************************************/ +extern "C" HRESULT DAPI UserCreateADsPath( + __in_z LPCWSTR wzObjectDomain, + __in_z LPCWSTR wzObjectName, + __out BSTR *pbstrAdsPath + ) +{ + Assert(wzObjectDomain && wzObjectName && *wzObjectName); + + HRESULT hr = S_OK; + LPWSTR pwzAdsPath = NULL; + + hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0); + UserExitOnFailure(hr, "failed to allocate AdsPath string"); + + if (*wzObjectDomain) + { + hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName); + UserExitOnFailure(hr, "failed to allocate AdsPath string"); + } + else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/")) + { + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); + UserExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName); + } + else + { + hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0); + UserExitOnFailure(hr, "failed to concat LocalHost/"); + + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); + UserExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName); + } + + *pbstrAdsPath = ::SysAllocString(pwzAdsPath); + if (NULL == *pbstrAdsPath) + { + hr = E_OUTOFMEMORY; + } + +LExit: + ReleaseStr(pwzAdsPath); + + return hr; +} + + +/******************************************************************* + Helper function to check if pwzGroupUserDomain (in form of "domain\username" is + a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the + output from both NetUserGetGroups() and NetUserGetLocalGroups() +********************************************************************/ +static BOOL CheckIsMemberHelper( + __in_z LPCWSTR pwzGroupUserDomain, + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, + __in DWORD cguiGroupData + ) +{ + if (NULL == pguiGroupData) + { + return FALSE; + } + + for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter) + { + // If the user is a member of the group, set the output flag to true + if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name)) + { + return TRUE; + } + } + + return FALSE; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) +#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) +#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) +#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) +#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__) + +// constants +const DWORD GROW_RELEASE_LABELS = 3; + +// Forward declarations. +static int CompareDword( + __in const DWORD& dw1, + __in const DWORD& dw2 + ); +static HRESULT CompareReleaseLabel( + __in const VERUTIL_VERSION_RELEASE_LABEL* p1, + __in LPCWSTR wzVersion1, + __in const VERUTIL_VERSION_RELEASE_LABEL* p2, + __in LPCWSTR wzVersion2, + __out int* pnResult + ); +static HRESULT CompareVersionSubstring( + __in LPCWSTR wzString1, + __in int cchCount1, + __in LPCWSTR wzString2, + __in int cchCount2, + __out int* pnResult + ); + + +DAPI_(HRESULT) VerCompareParsedVersions( + __in_opt VERUTIL_VERSION* pVersion1, + __in_opt VERUTIL_VERSION* pVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + DWORD cMaxReleaseLabels = 0; + BOOL fCompareMetadata = FALSE; + + if (pVersion1 && !pVersion1->sczVersion || + pVersion2 && !pVersion2->sczVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + + if (pVersion1 == pVersion2) + { + ExitFunction1(nResult = 0); + } + else if (pVersion1 && !pVersion2) + { + ExitFunction1(nResult = 1); + } + else if (!pVersion1 && pVersion2) + { + ExitFunction1(nResult = -1); + } + + nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision); + if (0 != nResult) + { + ExitFunction(); + } + + if (pVersion1->cReleaseLabels) + { + if (pVersion2->cReleaseLabels) + { + cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels); + } + else + { + ExitFunction1(nResult = -1); + } + } + else if (pVersion2->cReleaseLabels) + { + ExitFunction1(nResult = 1); + } + + if (cMaxReleaseLabels) + { + for (DWORD i = 0; i < cMaxReleaseLabels; ++i) + { + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL; + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL; + + hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult); + if (FAILED(hr) || 0 != nResult) + { + ExitFunction(); + } + } + } + + if (pVersion1->fInvalid) + { + if (!pVersion2->fInvalid) + { + ExitFunction1(nResult = -1); + } + else + { + fCompareMetadata = TRUE; + } + } + else if (pVersion2->fInvalid) + { + ExitFunction1(nResult = 1); + } + + if (fCompareMetadata) + { + hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult); + } + +LExit: + *pnResult = nResult; + return hr; +} + +DAPI_(HRESULT) VerCompareStringVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __in BOOL fStrict, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + int nResult = 0; + + hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1); + VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2); + VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2); + + hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); + VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2); + +LExit: + *pnResult = nResult; + + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + + return hr; +} + +DAPI_(HRESULT) VerCopyVersion( + __in VERUTIL_VERSION* pSource, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pCopy = NULL; + + pCopy = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy."); + + hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0); + VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion); + + pCopy->dwMajor = pSource->dwMajor; + pCopy->dwMinor = pSource->dwMinor; + pCopy->dwPatch = pSource->dwPatch; + pCopy->dwRevision = pSource->dwRevision; + + if (pSource->cReleaseLabels) + { + hr = MemEnsureArraySize(reinterpret_cast(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels); + VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies."); + + pCopy->cReleaseLabels = pSource->cReleaseLabels; + + for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i) + { + VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i; + VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i; + + pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset; + pCopyLabel->cchLabel = pSourceLabel->cchLabel; + pCopyLabel->fNumeric = pSourceLabel->fNumeric; + pCopyLabel->dwValue = pSourceLabel->dwValue; + } + } + + pCopy->cchMetadataOffset = pSource->cchMetadataOffset; + pCopy->fInvalid = pSource->fInvalid; + + *ppVersion = pCopy; + pCopy = NULL; + +LExit: + ReleaseVerutilVersion(pCopy); + + return hr; +} + +DAPI_(void) VerFreeVersion( + __in VERUTIL_VERSION* pVersion + ) +{ + if (pVersion) + { + ReleaseStr(pVersion->sczVersion); + ReleaseMem(pVersion->rgReleaseLabels); + ReleaseMem(pVersion); + } +} + +DAPI_(HRESULT) VerParseVersion( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __in BOOL fStrict, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + LPCWSTR wzEnd = NULL; + LPCWSTR wzPartBegin = NULL; + LPCWSTR wzPartEnd = NULL; + BOOL fInvalid = FALSE; + BOOL fLastPart = FALSE; + BOOL fTrailingDot = FALSE; + BOOL fParsedVersionNumber = FALSE; + BOOL fExpectedReleaseLabels = FALSE; + DWORD iPart = 0; + + if (!wzVersion || !ppVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + + // Get string length if not provided. + if (!cchVersion) + { + hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); + VerExitOnRootFailure(hr, "Failed to get length of version string: %ls", wzVersion); + } + else if (INT_MAX < cchVersion) + { + VerExitOnRootFailure(hr = E_INVALIDARG, "Version string is too long: %Iu", cchVersion); + } + + if (L'v' == *wzVersion || L'V' == *wzVersion) + { + ++wzVersion; + --cchVersion; + } + + pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion); + + hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion); + VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion); + + wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion; + + // Save end pointer. + wzEnd = wzVersion + cchVersion; + + // Parse version number + while (wzPartBegin < wzEnd) + { + fTrailingDot = FALSE; + + // Find end of part. + for (;;) + { + if (wzPartEnd >= wzEnd) + { + fLastPart = TRUE; + break; + } + + switch (*wzPartEnd) + { + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + ++wzPartEnd; + continue; + case L'.': + fTrailingDot = TRUE; + break; + case L'-': + case L'+': + fLastPart = TRUE; + break; + default: + fInvalid = TRUE; + break; + } + + break; + } + + if (wzPartBegin == wzPartEnd) + { + fInvalid = TRUE; + } + + if (fInvalid) + { + break; + } + + DWORD cchPart = 0; + hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); + if (FAILED(hr)) + { + fInvalid = TRUE; + break; + } + + // Parse version part. + UINT uPart = 0; + hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart); + if (FAILED(hr)) + { + fInvalid = TRUE; + break; + } + + switch (iPart) + { + case 0: + pVersion->dwMajor = uPart; + break; + case 1: + pVersion->dwMinor = uPart; + break; + case 2: + pVersion->dwPatch = uPart; + break; + case 3: + pVersion->dwRevision = uPart; + break; + } + + if (fTrailingDot) + { + ++wzPartEnd; + } + wzPartBegin = wzPartEnd; + ++iPart; + + if (4 <= iPart || fLastPart) + { + fParsedVersionNumber = TRUE; + break; + } + } + + fInvalid |= !fParsedVersionNumber || fTrailingDot; + + if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-') + { + wzPartBegin = wzPartEnd = wzPartBegin + 1; + fExpectedReleaseLabels = TRUE; + fLastPart = FALSE; + } + + while (fExpectedReleaseLabels && wzPartBegin < wzEnd) + { + fTrailingDot = FALSE; + + // Find end of part. + for (;;) + { + if (wzPartEnd >= wzEnd) + { + fLastPart = TRUE; + break; + } + + if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' || + *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' || + *wzPartEnd >= L'a' && *wzPartEnd <= L'z' || + *wzPartEnd == L'-') + { + ++wzPartEnd; + continue; + } + else if (*wzPartEnd == L'+') + { + fLastPart = TRUE; + } + else if (*wzPartEnd == L'.') + { + fTrailingDot = TRUE; + } + else + { + fInvalid = TRUE; + } + + break; + } + + if (wzPartBegin == wzPartEnd) + { + fInvalid = TRUE; + } + + if (fInvalid) + { + break; + } + + int cchLabel = 0; + hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel); + if (FAILED(hr) || 0 > cchLabel) + { + fInvalid = TRUE; + break; + } + + hr = MemReAllocArray(reinterpret_cast(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS)); + VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion); + + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels; + ++pVersion->cReleaseLabels; + + // Try to parse as number. + UINT uLabel = 0; + hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel); + if (SUCCEEDED(hr)) + { + pReleaseLabel->fNumeric = TRUE; + pReleaseLabel->dwValue = uLabel; + } + + pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion; + pReleaseLabel->cchLabel = cchLabel; + + if (fTrailingDot) + { + ++wzPartEnd; + } + wzPartBegin = wzPartEnd; + + if (fLastPart) + { + break; + } + } + + fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot); + + if (!fInvalid && wzPartBegin < wzEnd) + { + if (*wzPartBegin == L'+') + { + wzPartBegin = wzPartEnd = wzPartBegin + 1; + } + else + { + fInvalid = TRUE; + } + } + + if (fInvalid && fStrict) + { + ExitFunction1(hr = E_INVALIDARG); + } + + pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion; + pVersion->fInvalid = fInvalid; + + *ppVersion = pVersion; + pVersion = NULL; + hr = S_OK; + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +DAPI_(HRESULT) VerVersionFromQword( + __in DWORD64 qwVersion, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD."); + + pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff); + pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff); + pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff); + pVersion->dwRevision = (WORD)(qwVersion & 0xffff); + + hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision); + ExitOnFailure(hr, "Failed to allocate and format the version string."); + + pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion); + + *ppVersion = pVersion; + pVersion = NULL; + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + + +static int CompareDword( + __in const DWORD& dw1, + __in const DWORD& dw2 + ) +{ + int nResult = 0; + + if (dw1 > dw2) + { + nResult = 1; + } + else if (dw1 < dw2) + { + nResult = -1; + } + + return nResult; +} + +static HRESULT CompareReleaseLabel( + __in const VERUTIL_VERSION_RELEASE_LABEL* p1, + __in LPCWSTR wzVersion1, + __in const VERUTIL_VERSION_RELEASE_LABEL* p2, + __in LPCWSTR wzVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + + if (p1 == p2) + { + ExitFunction(); + } + else if (p1 && !p2) + { + ExitFunction1(nResult = 1); + } + else if (!p1 && p2) + { + ExitFunction1(nResult = -1); + } + + if (p1->fNumeric) + { + if (p2->fNumeric) + { + nResult = CompareDword(p1->dwValue, p2->dwValue); + } + else + { + nResult = -1; + } + } + else + { + if (p2->fNumeric) + { + nResult = 1; + } + else + { + hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult); + } + } + +LExit: + *pnResult = nResult; + + return hr; +} + +static HRESULT CompareVersionSubstring( + __in LPCWSTR wzString1, + __in int cchCount1, + __in LPCWSTR wzString2, + __in int cchCount2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + + nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2); + if (!nResult) + { + VerExitOnLastError(hr, "Failed to compare version substrings"); + } + +LExit: + *pnResult = nResult - 2; + + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) +#define WiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) +#define WiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) +#define WiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) +#define WiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WIUTIL, e, x, s, __VA_ARGS__) +#define WiuExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WIUTIL, g, x, s, __VA_ARGS__) + + +// constants + +const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF; +const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64; + + +// structs + + +static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW; +static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW; +static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW; +static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW; +static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW; +static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW; +static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW; +static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW; +static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI; +static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW; +static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW; +static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW; + +// MSI 3.0+ +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL; +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL; +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL; +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL; +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL; +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL; +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; + +static HMODULE vhMsiDll = NULL; +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL; +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL; +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL; + +// MSI Transactions v4.5+ +static PFN_MSIBEGINTRANSACTIONW vpfnMsiBeginTransaction = NULL; +static PFN_MSIENDTRANSACTION vpfnMsiEndTransaction = NULL; + +static BOOL vfWiuInitialized = FALSE; + +// globals +static DWORD vdwMsiDllMajorMinor = 0; +static DWORD vdwMsiDllBuildRevision = 0; + + +// internal function declarations + +static DWORD CheckForRestartErrorCode( + __in DWORD dwErrorCode, + __out WIU_RESTART* pRestart + ); +static INT CALLBACK InstallEngineCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_z_opt LPCWSTR wzMessage + ); +static INT CALLBACK InstallEngineRecordCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_opt MSIHANDLE hRecord + ); +static INT HandleInstallMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT HandleInstallProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_z_opt LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendMsiMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendErrorMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendFilesInUseMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_opt MSIHANDLE hRecord, + __in BOOL fRestartManagerRequest + ); +static INT SendProgressUpdate( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ); +static void ResetProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ); +static DWORD CalculatePhaseProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in DWORD dwProgressIndex, + __in DWORD dwWeightPercentage + ); +void InitializeMessageData( + __in_opt MSIHANDLE hRecord, + __deref_out_ecount(*pcData) LPWSTR** prgsczData, + __out DWORD* pcData + ); +void UninitializeMessageData( + __in LPWSTR* rgsczData, + __in DWORD cData + ); + + +/******************************************************************** + WiuInitialize - initializes wiutil + +*********************************************************************/ +extern "C" HRESULT DAPI WiuInitialize( + ) +{ + HRESULT hr = S_OK; + LPWSTR sczMsiDllPath = NULL; + + hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath); + WiuExitOnFailure(hr, "Failed to load Msi.DLL"); + + // Ignore failures + FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision); + + vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW")); + if (NULL == vpfnMsiDeterminePatchSequenceW) + { + vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary; + } + + vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW")); + if (NULL == vpfnMsiDetermineApplicablePatchesW) + { + vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary; + } + + vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW")); + if (NULL == vpfnMsiEnumProductsExW) + { + vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary; + } + + vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW")); + if (NULL == vpfnMsiGetPatchInfoExW) + { + vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary; + } + + vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW")); + if (NULL == vpfnMsiGetProductInfoExW) + { + vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary; + } + + vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord")); + if (NULL == vpfnMsiSetExternalUIRecord) + { + vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary; + } + + //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; + vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW")); + if (NULL == vpfnMsiSourceListAddSourceExW) + { + vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary; + } + + // MSI Transaction functions + if (NULL == vpfnMsiBeginTransaction) + { + vpfnMsiBeginTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiBeginTransactionW")); + } + + if (NULL == vpfnMsiEndTransaction) + { + vpfnMsiEndTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEndTransaction")); + } + + vfWiuInitialized = TRUE; + +LExit: + ReleaseStr(sczMsiDllPath); + return hr; +} + + +/******************************************************************** + WiuUninitialize - uninitializes wiutil + +*********************************************************************/ +extern "C" void DAPI WiuUninitialize( + ) +{ + if (vhMsiDll) + { + ::FreeLibrary(vhMsiDll); + vhMsiDll = NULL; + vpfnMsiSetExternalUIRecordFromLibrary = NULL; + vpfnMsiGetProductInfoExWFromLibrary = NULL; + vpfnMsiGetPatchInfoExWFromLibrary = NULL; + vpfnMsiEnumProductsExWFromLibrary = NULL; + vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; + vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; + vpfnMsiSourceListAddSourceExWFromLibrary = NULL; + vpfnMsiBeginTransaction = NULL; + vpfnMsiEndTransaction = NULL; + } + + vfWiuInitialized = FALSE; +} + + +/******************************************************************** + WiuFunctionOverride - overrides the Windows installer functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI WiuFunctionOverride( + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW + ) +{ + vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW; + vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW; + vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW; + vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW; + vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW; + vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW; + vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW; + vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI; + vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW; + vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW; + vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary; + vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary; + vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary; +} + + +extern "C" HRESULT DAPI WiuGetComponentPath( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + DWORD cchCompare; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + if (INSTALLSTATE_MOREDATA == *pInstallState) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + } + + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + WiuExitOnRootFailure(hr, "Invalid argument when getting component path."); + } + else if (INSTALLSTATE_UNKNOWN == *pInstallState) + { + ExitFunction(); + } + + // If the actual path length is greater than or equal to the original buffer + // allocate a larger buffer and get the path again, just in case we are + // missing any part of the path. + if (cchCompare <= cch) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuLocateComponent( + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + DWORD cchCompare; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + if (INSTALLSTATE_MOREDATA == *pInstallState) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + } + + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + WiuExitOnRootFailure(hr, "Invalid argument when locating component."); + } + else if (INSTALLSTATE_UNKNOWN == *pInstallState) + { + ExitFunction(); + } + + // If the actual path length is greater than or equal to the original buffer + // allocate a larger buffer and get the path again, just in case we are + // missing any part of the path. + if (cchCompare <= cch) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuQueryFeatureState( + __in_z LPCWSTR wzProduct, + __in_z LPCWSTR wzFeature, + __out INSTALLSTATE* pInstallState + ) +{ + HRESULT hr = S_OK; + + *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature); + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + WiuExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductInfo( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for product info."); + + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for product info."); + + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get product info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductInfoEx( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + if (!vpfnMsiGetProductInfoExW) + { + hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue); + WiuExitOnFailure(hr, "Failed to get product info when extended info was not available."); + + ExitFunction(); + } + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for extended product info."); + + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for extended product info."); + + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get extended product info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductProperty( + __in MSIHANDLE hProduct, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for product property."); + + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for product property."); + + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get product property."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetPatchInfoEx( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + if (!vpfnMsiGetPatchInfoExW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for extended patch info."); + + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for extended patch info."); + + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get extended patch info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuDeterminePatchSequence( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiDeterminePatchSequenceW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo); + WiuExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuDetermineApplicablePatches( + __in_z LPCWSTR wzProductPackagePath, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiDetermineApplicablePatchesW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); + WiuExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumProducts( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumProductsEx( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiEnumProductsExW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumRelatedProducts( + __in_z LPCWSTR wzUpgradeCode, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + WiuExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); + +LExit: + return hr; +} + +/******************************************************************** + WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code. + + Parameters: + wzUpgradeCode - The upgrade code that will be used to find the related products. + prgsczProductCodes - Pointer to the array that will contain the product codes. + pcRelatedProducts - Returns the count of the number of related products found. + fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found. +********************************************************************/ +extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( + __in_z LPCWSTR wzUpgradeCode, + __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, + __out DWORD* pcRelatedProducts, + __in BOOL fReturnHighestVersionOnly + ) +{ + HRESULT hr = S_OK; + WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { }; + LPWSTR sczInstalledVersion = NULL; + VERUTIL_VERSION* pCurrentVersion = NULL; + VERUTIL_VERSION* pHighestVersion = NULL; + int nCompare = 0; + + // make sure we start at zero + *pcRelatedProducts = 0; + + for (DWORD i = 0; ; ++i) + { + hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode); + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + WiuExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); + + if (fReturnHighestVersionOnly) + { + // try to get the version but if the product registration is broken + // (for whatever reason), skip this product + hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); + if (FAILED(hr)) + { + WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode); + continue; + } + + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCurrentVersion); + WiuExitOnFailure(hr, "Failed to parse version: %ls for product code: %ls", sczInstalledVersion, wzCurrentProductCode); + + if (pCurrentVersion->fInvalid) + { + WiuExitTrace(E_INVALIDDATA, "Enumerated msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'"); + } + + // if this is the first product found then it is the highest version (for now) + if (!pHighestVersion) + { + pHighestVersion = pCurrentVersion; + pCurrentVersion = NULL; + } + else + { + hr = VerCompareParsedVersions(pCurrentVersion, pHighestVersion, &nCompare); + WiuExitOnFailure(hr, "Failed to compare version '%ls' to highest version: '%ls'", pCurrentVersion->sczVersion, pHighestVersion->sczVersion); + + // if this is the highest version encountered so far then overwrite + // the first item in the array (there will never be more than one item) + if (nCompare > 0) + { + ReleaseVerutilVersion(pHighestVersion); + pHighestVersion = pCurrentVersion; + pCurrentVersion = NULL; + + hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); + WiuExitOnFailure(hr, "Failed to update array with higher versioned product code."); + } + else + { + ReleaseVerutilVersion(pCurrentVersion); + } + + // continue here as we don't want anything else added to the list + continue; + } + } + + hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0); + WiuExitOnFailure(hr, "Failed to add product code to array."); + } + +LExit: + ReleaseVerutilVersion(pCurrentVersion); + ReleaseVerutilVersion(pHighestVersion); + ReleaseStr(sczInstalledVersion); + return hr; +} + + +extern "C" HRESULT DAPI WiuEnableLog( + __in DWORD dwLogMode, + __in_z LPCWSTR wzLogFile, + __in DWORD dwLogAttributes + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes); + WiuExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuInitializeInternalUI( + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + HRESULT hr = S_OK; + + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); + + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); + pExecuteContext->hwndPreviousParentWindow = hwndParent; + + if (INSTALLUILEVEL_NOCHANGE == pExecuteContext->previousInstallUILevel) + { + hr = E_INVALIDARG; + } + + return hr; +} + + +extern "C" HRESULT DAPI WiuInitializeExternalUI( + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in LPVOID pvContext, + __in BOOL fRollback, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | + INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | + INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | + INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | + INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE; + + if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor) + { + dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE; + } + + // Wire the internal and external UI handler. + hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext); + WiuExitOnFailure(hr, "Failed to set internal UI level and window."); + + pExecuteContext->fRollback = fRollback; + pExecuteContext->pfnMessageHandler = pfnMessageHandler; + pExecuteContext->pvContext = pvContext; + + // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external + // UI handler if necesary. + if (vpfnMsiSetExternalUIRecord) + { + er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord); + WiuExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); + pExecuteContext->fSetPreviousExternalUIRecord = TRUE; + } + else + { + pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext); + pExecuteContext->fSetPreviousExternalUI = TRUE; + } + +LExit: + return hr; +} + + +extern "C" void DAPI WiuUninitializeExternalUI( + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel) + { + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow); + } + + if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler + { + vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL); + } + + if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler + { + vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL); + } + + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); +} + + +extern "C" HRESULT DAPI WiuConfigureProductEx( + __in_z LPCWSTR wzProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine); + er = CheckForRestartErrorCode(er, pRestart); + WiuExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuInstallProduct( + __in_z LPCWSTR wzPackagePath, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine); + er = CheckForRestartErrorCode(er, pRestart); + WiuExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuRemovePatches( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzPropertyList, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList); + er = CheckForRestartErrorCode(er, pRestart); + WiuExitOnWin32Error(er, hr, "Failed to remove patches."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuSourceListAddSourceEx( + __in_z LPCWSTR wzProductCodeOrPatchCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwCode, + __in_z LPCWSTR wzSource, + __in_opt DWORD dwIndex + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex); + WiuExitOnWin32Error(er, hr, "Failed to add source."); + +LExit: + return hr; +} + +extern "C" BOOL DAPI WiuIsMsiTransactionSupported( + ) +{ + return vpfnMsiBeginTransaction && vpfnMsiEndTransaction; +} + +extern "C" HRESULT DAPI WiuBeginTransaction( + __in_z LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE * phTransactionHandle, + __out HANDLE * phChangeOfOwnerEvent, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!WiuIsMsiTransactionSupported()) + { + WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + } + + hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); + WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + + er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent); + WiuExitOnWin32Error(er, hr, "Failed to begin transaction."); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI WiuEndTransaction( + __in DWORD dwTransactionState, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!WiuIsMsiTransactionSupported()) + { + WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + } + + hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); + WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + + er = vpfnMsiEndTransaction(dwTransactionState); + WiuExitOnWin32Error(er, hr, "Failed to end transaction."); + +LExit: + return hr; +} + + + +static DWORD CheckForRestartErrorCode( + __in DWORD dwErrorCode, + __out WIU_RESTART* pRestart + ) +{ + switch (dwErrorCode) + { + case ERROR_SUCCESS_REBOOT_REQUIRED: + case ERROR_SUCCESS_RESTART_REQUIRED: + *pRestart = WIU_RESTART_REQUIRED; + dwErrorCode = ERROR_SUCCESS; + break; + + case ERROR_SUCCESS_REBOOT_INITIATED: + case ERROR_INSTALL_SUSPEND: + *pRestart = WIU_RESTART_INITIATED; + dwErrorCode = ERROR_SUCCESS; + break; + } + + return dwErrorCode; +} + +static INT CALLBACK InstallEngineCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_z_opt LPCWSTR wzMessage + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; + INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); + UINT uiFlags = 0x00FFFFFF & uiMessage; + + if (wzMessage) + { + if (INSTALLMESSAGE_PROGRESS == mt) + { + nResult = HandleInstallProgress(pContext, wzMessage, NULL); + } + else + { + nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL); + } + } + + return nResult; +} + +static INT CALLBACK InstallEngineRecordCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + HRESULT hr = S_OK; + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; + + INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); + UINT uiFlags = 0x00FFFFFF & uiMessage; + LPWSTR sczMessage = NULL; + DWORD cchMessage = 0; + + if (hRecord) + { + if (INSTALLMESSAGE_PROGRESS == mt) + { + nResult = HandleInstallProgress(pContext, NULL, hRecord); + } + else + { + // create formated message string +#pragma prefast(push) +#pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size + DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage); +#pragma prefast(pop) + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) + { + hr = StrAlloc(&sczMessage, ++cchMessage); + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + WiuExitOnFailure(hr, "Failed to allocate string for formated message."); + + er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage); + WiuExitOnWin32Error(er, hr, "Failed to format message record."); + + // Pass to handler including both the formated message and the original record. + nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord); + } + } + +LExit: + ReleaseStr(sczMessage); + return nResult; +} + +static INT HandleInstallMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + +Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage); + + // Handle the message. + switch (mt) + { + case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data + ResetProgress(pContext); + break; + + case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data + break; + + case INSTALLMESSAGE_ACTIONSTART: + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + } + + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + break; + + case INSTALLMESSAGE_ACTIONDATA: + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) + { + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; + } + else // rollback. + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; + } + + nResult = SendProgressUpdate(pContext); + } + else + { + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + } + break; + + case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough; + case INSTALLMESSAGE_FATALEXIT: __fallthrough; + case INSTALLMESSAGE_ERROR: + nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord); + break; + + case INSTALLMESSAGE_FILESINUSE: + case INSTALLMESSAGE_RMFILESINUSE: + nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt); + break; + +/* +#if 0 + case INSTALLMESSAGE_COMMONDATA: + if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2]) + { + if (L'0' == wzMessage[3]) + { + // TODO: handle the language common data message. + lres = IDOK; + return lres; + } + else if (L'1' == wzMessage[3]) + { + // TODO: really handle sending the caption. + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast(wzMessage + 3)); + return lres; + } + else if (L'2' == wzMessage[3]) + { + // TODO: really handle sending the cancel button status. + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast(wzMessage + 3)); + return lres; + } + } + break; +#endif +*/ + + //case INSTALLMESSAGE_WARNING: + //case INSTALLMESSAGE_USER: + //case INSTALLMESSAGE_INFO: + //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard + default: + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + break; + } + + // Always return "no action" (0) for resolve source messages. + return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult; +} + +static INT HandleInstallProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_z_opt LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + HRESULT hr = S_OK; + INT nResult = IDNOACTION; + INT iFields[4] = { }; + INT cFields = 0; + LPCWSTR pwz = NULL; + DWORD cch = 0; + + // get field values + if (hRecord) + { + cFields = ::MsiRecordGetFieldCount(hRecord); + cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold + for (INT i = 0; i < cFields; ++i) + { + iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1); + } + } + else + { + Assert(wzMessage); + + // parse message string + pwz = wzMessage; + while (cFields < 4) + { + // check if we have the start of a valid part + if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2]) + { + break; + } + pwz += 3; + + // find character count of number + cch = 0; + while (pwz[cch] && L' ' != pwz[cch]) + { + ++cch; + } + + // parse number + hr = StrStringToInt32(pwz, cch, &iFields[cFields]); + WiuExitOnFailure(hr, "Failed to parse MSI message part."); + + // increment field count + ++cFields; + } + } + +#ifdef _DEBUG + WCHAR wz[256]; + ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]); + Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz); +#endif + + // Verify that we have the enough field values. + if (1 > cFields) + { + ExitFunction(); // unknown message, bail + } + + // Handle based on message type. + switch (iFields[0]) + { + case 0: // master progress reset + if (4 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + ExitFunction(); + } + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + // Update the index into progress array. + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) + { + pContext->dwCurrentProgressIndex = 0; + } + else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress)) + { + ++pContext->dwCurrentProgressIndex; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + WiuExitOnRootFailure(hr, "Insufficient space to hold progress information."); + } + + // we only care about the first stage after script execution has started + //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3]) + //{ + // pEngineInfo->fMsiProgressFinished = TRUE; + //} + + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1]; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]); + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]); + + if (0 == pContext->dwCurrentProgressIndex) + { + // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase + // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress + // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this + // "close" and deal with the rest. + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50; + } + break; + + case 1: // action info. + if (3 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + ExitFunction(); + } + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + if (0 == iFields[2]) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + } + else + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1]; + } + break; + + case 2: // progress report. + if (2 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + break; + } + + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) + { + break; + } + else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal) + { + break; + } + + // Update progress. + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1]; + } + else // rollback. + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1]; + } + break; + + case 3: // extend the progress bar. + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1]; + break; + + default: + ExitFunction(); // unknown message, bail + } + + // If we have a valid progress index, send an update. + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex) + { + nResult = SendProgressUpdate(pContext); + } + +LExit: + return nResult; +} + +static INT SendMsiMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; + message.dwAllowedResults = uiFlags; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.msiMessage.mt = mt; + message.msiMessage.wzMessage = wzMessage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendErrorMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + DWORD dwErrorCode = 0; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + if (hRecord) + { + dwErrorCode = ::MsiRecordGetInteger(hRecord, 1); + + // Set the recommendation if it's a known error code. + switch (dwErrorCode) + { + case 1605: // continue with install even if there isn't enough room for rollback. + nResult = IDIGNORE; + break; + + case 1704: // rollback suspended installs so our install can continue. + nResult = IDOK; + break; + } + } + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; + message.dwAllowedResults = uiFlags; + message.nResultRecommendation = nResult; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.error.dwErrorCode = dwErrorCode; + message.error.wzMessage = wzMessage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendFilesInUseMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_opt MSIHANDLE hRecord, + __in BOOL /*fRestartManagerRequest*/ + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; + message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information. + message.msiFilesInUse.rgwzFiles = message.rgwzData; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendProgressUpdate( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ) +{ + int nResult = IDNOACTION; + DWORD dwPercentage = 0; // number representing 0 - 100% + WIU_MSI_EXECUTE_MESSAGE message = { }; + + //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal; + //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete); + //double dProgressGauge = 0; + //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal; + + // Calculate progress for the phases of Windows Installer. + // TODO: handle upgrade progress which would add another phase. + dwPercentage += CalculatePhaseProgress(pContext, 0, 15); + dwPercentage += CalculatePhaseProgress(pContext, 1, 80); + dwPercentage += CalculatePhaseProgress(pContext, 2, 5); + dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%. + + if (pContext->fRollback) + { + dwPercentage = 100 - dwPercentage; + } + + //if (qwTotal) // avoid "divide by zero" if the MSI range is blank. + //{ + // // calculate gauge. + // double dProgressGauge = static_cast(qwCompleted) / static_cast(qwTotal); + // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804; + // qwCompleted = (DWORD)(dProgressGauge * qwTotal); + + // // calculate progress within range + // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal)); + // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal); + //} + +#ifdef _DEBUG + DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted; + DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal; + Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage); + //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress."); +#endif + + message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = dwPercentage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + return nResult; +} + +static void ResetProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ) +{ + memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress)); + pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID; +} + +static DWORD CalculatePhaseProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in DWORD dwProgressIndex, + __in DWORD dwWeightPercentage + ) +{ + DWORD dwPhasePercentage = 0; + + // If we've already passed this progress index, return the maximum percentage possible (the weight) + if (dwProgressIndex < pContext->dwCurrentProgressIndex) + { + dwPhasePercentage = dwWeightPercentage; + } + else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress. + { + WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex; + if (pProgress->dwTotal) + { + DWORD64 dw64Completed = pProgress->dwCompleted; + dwPhasePercentage = static_cast(dw64Completed * dwWeightPercentage / pProgress->dwTotal); + } + } + // else we're not there yet so it has to be zero. + + return dwPhasePercentage; +} + +void InitializeMessageData( + __in_opt MSIHANDLE hRecord, + __deref_out_ecount(*pcData) LPWSTR** prgsczData, + __out DWORD* pcData + ) +{ + DWORD cData = 0; + LPWSTR* rgsczData = NULL; + + // If we have a record based message, try to get the extra data. + if (hRecord) + { + cData = ::MsiRecordGetFieldCount(hRecord); + if (cData) + { + rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE); + } + + for (DWORD i = 0; rgsczData && i < cData; ++i) + { + DWORD cch = 0; + + // get string from record +#pragma prefast(push) +#pragma prefast(disable:6298) + DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch); +#pragma prefast(pop) + if (ERROR_MORE_DATA == er) + { + HRESULT hr = StrAlloc(&rgsczData[i], ++cch); + if (SUCCEEDED(hr)) + { + er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch); + } + } + } + } + + *prgsczData = rgsczData; + *pcData = cData; +} + +void UninitializeMessageData( + __in LPWSTR* rgsczData, + __in DWORD cData + ) +{ + // Clean up if there was any data allocated. + if (rgsczData) + { + for (DWORD i = 0; i < cData; ++i) + { + ReleaseStr(rgsczData[i]); + } + + MemFree(rgsczData); + } +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define WuaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) +#define WuaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) +#define WuaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) +#define WuaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) +#define WuaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WUAUTIL, e, x, s, __VA_ARGS__) +#define WuaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WUAUTIL, g, x, s, __VA_ARGS__) + + +// internal function declarations + +static HRESULT GetAutomaticUpdatesService( + __out IAutomaticUpdates **ppAutomaticUpdates + ); + + +// function definitions + +extern "C" HRESULT DAPI WuaPauseAutomaticUpdates() +{ + HRESULT hr = S_OK; + IAutomaticUpdates *pAutomaticUpdates = NULL; + + hr = GetAutomaticUpdatesService(&pAutomaticUpdates); + WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); + + hr = pAutomaticUpdates->Pause(); + WuaExitOnFailure(hr, "Failed to pause the Automatic Updates service."); + +LExit: + ReleaseObject(pAutomaticUpdates); + + return hr; +} + +extern "C" HRESULT DAPI WuaResumeAutomaticUpdates() +{ + HRESULT hr = S_OK; + IAutomaticUpdates *pAutomaticUpdates = NULL; + + hr = GetAutomaticUpdatesService(&pAutomaticUpdates); + WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); + + hr = pAutomaticUpdates->Resume(); + WuaExitOnFailure(hr, "Failed to resume the Automatic Updates service."); + +LExit: + ReleaseObject(pAutomaticUpdates); + + return hr; +} + +extern "C" HRESULT DAPI WuaRestartRequired( + __out BOOL* pfRestartRequired + ) +{ + HRESULT hr = S_OK; + ISystemInformation* pSystemInformation = NULL; + VARIANT_BOOL bRestartRequired; + + hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast(&pSystemInformation)); + WuaExitOnRootFailure(hr, "Failed to get WUA system information interface."); + + hr = pSystemInformation->get_RebootRequired(&bRestartRequired); + WuaExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); + + *pfRestartRequired = (VARIANT_FALSE != bRestartRequired); + +LExit: + ReleaseObject(pSystemInformation); + + return hr; +} + + +// internal function definitions + +static HRESULT GetAutomaticUpdatesService( + __out IAutomaticUpdates **ppAutomaticUpdates + ) +{ + HRESULT hr = S_OK; + CLSID clsidAutomaticUpdates = { }; + + hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates); + WuaExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate."); + + hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast(ppAutomaticUpdates)); + WuaExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate."); + +LExit: + return hr; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define XmlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) +#define XmlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) +#define XmlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) +#define XmlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) +#define XmlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_XMLUTIL, e, x, s, __VA_ARGS__) +#define XmlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_XMLUTIL, g, x, s, __VA_ARGS__) + +// intialization globals +CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} }; +static volatile LONG vcXmlInitialized = 0; +static BOOL vfMsxml40 = FALSE; +static BOOL fComInitialized = FALSE; +BOOL vfMsxml30 = FALSE; + +/******************************************************************** + XmlInitialize - finds an appropriate version of the XML DOM + +*********************************************************************/ +extern "C" HRESULT DAPI XmlInitialize( + ) +{ + HRESULT hr = S_OK; + + if (!fComInitialized) + { + hr = ::CoInitialize(0); + if (RPC_E_CHANGED_MODE != hr) + { + XmlExitOnFailure(hr, "failed to initialize COM"); + fComInitialized = TRUE; + } + } + + LONG cInitialized = ::InterlockedIncrement(&vcXmlInitialized); + if (1 == cInitialized) + { + // NOTE: 4.0 behaves differently than 3.0 so there may be problems doing this +#if 0 + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument.4.0", &vclsidXMLDOM); + if (S_OK == hr) + { + vfMsxml40 = TRUE; + Trace(REPORT_VERBOSE, "found Msxml2.DOMDocument.4.0"); + ExitFunction(); + } +#endif + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument", &vclsidXMLDOM); + if (FAILED(hr)) + { + // try to fall back to old MSXML + hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM); + } + XmlExitOnFailure(hr, "failed to get CLSID for XML DOM"); + + Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument26) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument40) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument50) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument60)); + } + + hr = S_OK; +LExit: + return hr; +} + + +/******************************************************************** + XmUninitialize - + +*********************************************************************/ +extern "C" void DAPI XmlUninitialize( + ) +{ + AssertSz(vcXmlInitialized, "XmlUninitialize called when not initialized"); + + LONG cInitialized = ::InterlockedDecrement(&vcXmlInitialized); + + if (0 == cInitialized) + { + memset(&vclsidXMLDOM, 0, sizeof(vclsidXMLDOM)); + + if (fComInitialized) + { + ::CoUninitialize(); + } + } +} + +extern "C" HRESULT DAPI XmlCreateElement( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzElementName, + __out IXMLDOMElement **ppixnElement + ) +{ + if (!ppixnElement || !pixdDocument) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrElementName = ::SysAllocString(wzElementName); + XmlExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + hr = pixdDocument->createElement(bstrElementName, ppixnElement); +LExit: + ReleaseBSTR(bstrElementName); + return hr; +} + + +/******************************************************************** + XmlCreateDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateDocument( + __in_opt LPCWSTR pwzElementName, + __out IXMLDOMDocument** ppixdDocument, + __out_opt IXMLDOMElement** ppixeRootElement + ) +{ + HRESULT hr = S_OK; + BOOL (WINAPI *pfnDisableWow64)(__out PVOID* ) = NULL; + BOOLEAN (WINAPI *pfnEnableWow64)(__in BOOLEAN ) = NULL; + BOOL (WINAPI *pfnRevertWow64)(__in PVOID ) = NULL; + BOOL fWow64Available = FALSE; + void *pvWow64State = NULL; + + // RELEASEME + IXMLDOMElement* pixeRootElement = NULL; + IXMLDOMDocument *pixdDocument = NULL; + + // Test if we have access to the Wow64 API, and store the result in fWow64Available + HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll"); + XmlExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll"); + + // This will test if we have access to the Wow64 API + if (NULL != GetProcAddress(hKernel32, "IsWow64Process")) + { + pfnDisableWow64 = (BOOL (WINAPI *)(PVOID *))::GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection"); + pfnEnableWow64 = (BOOLEAN (WINAPI *)(BOOLEAN))::GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection"); + pfnRevertWow64 = (BOOL (WINAPI *)(PVOID))::GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection"); + + fWow64Available = pfnDisableWow64 && pfnEnableWow64 && pfnRevertWow64; + } + + // create the top level XML document + AssertSz(vcXmlInitialized, "XmlInitialize() was not called"); + + // Enable Wow64 Redirection, if possible + if (fWow64Available) + { + // 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) + pfnDisableWow64(&pvWow64State); + // If we fail to enable it, don't bother trying to disable it later on + fWow64Available = pfnEnableWow64(TRUE); + } + + hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument); + XmlExitOnFailure(hr, "failed to create XML DOM Document"); + Assert(pixdDocument); + + if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20)) + { + vfMsxml30 = TRUE; + } + + if (pwzElementName) + { + hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement); + XmlExitOnFailure(hr, "failed XmlCreateElement"); + hr = pixdDocument->appendChild(pixeRootElement, NULL); + XmlExitOnFailure(hr, "failed appendChild"); + } + + *ppixdDocument = pixdDocument; + pixdDocument = NULL; + + if (ppixeRootElement) + { + *ppixeRootElement = pixeRootElement; + pixeRootElement = NULL; + } + +LExit: + // Re-disable Wow64 Redirection, if appropriate + if (fWow64Available && !pfnRevertWow64(pvWow64State)) + { + // If we expected to be able to revert, and couldn't, fail in the only graceful way we can + ::ExitProcess(1); + } + + ReleaseObject(pixeRootElement); + ReleaseObject(pixdDocument); + return hr; +} + + +/******************************************************************** + XmlLoadDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocument( + __in_z LPCWSTR wzDocument, + __out IXMLDOMDocument** ppixdDocument + ) +{ + return XmlLoadDocumentEx(wzDocument, 0, ppixdDocument); +} + + +/******************************************************************** + XmlReportParseError - + +*********************************************************************/ +static void XmlReportParseError( + __in IXMLDOMParseError* pixpe + ) +{ + HRESULT hr = S_OK; + long lNumber = 0; + BSTR bstr = NULL; + + Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:"); + + hr = pixpe->get_errorCode(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode."); + Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber); + + hr = pixpe->get_filepos(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos."); + Trace(REPORT_STANDARD, "filepos = %d", lNumber); + + hr = pixpe->get_line(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.line."); + Trace(REPORT_STANDARD, "line = %d", lNumber); + + hr = pixpe->get_linepos(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos."); + Trace(REPORT_STANDARD, "linepos = %d", lNumber); + + hr = pixpe->get_reason(&bstr); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason."); + Trace(REPORT_STANDARD, "reason = %ls", bstr); + ReleaseNullBSTR(bstr); + + hr = pixpe->get_srcText (&bstr); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText ."); + Trace(REPORT_STANDARD, "srcText = %ls", bstr); + ReleaseNullBSTR(bstr); + +LExit: + ReleaseBSTR(bstr); +} + +/******************************************************************** + XmlLoadDocumentEx - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentEx( + __in_z LPCWSTR wzDocument, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + VARIANT_BOOL vbSuccess = 0; + + // RELEASEME + IXMLDOMDocument* pixd = NULL; + IXMLDOMParseError* pixpe = NULL; + BSTR bstrLoad = NULL; + + if (!wzDocument || !*wzDocument) + { + hr = E_UNEXPECTED; + XmlExitOnFailure(hr, "string must be non-null"); + } + + hr = XmlCreateDocument(NULL, &pixd); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed XmlCreateDocument"); + + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) + { + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); + XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); + } + + // Security issue. Avoid triggering anything external. + hr = pixd->put_validateOnParse(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixd->put_resolveExternals(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_resolveExternals"); + + bstrLoad = ::SysAllocString(wzDocument); + XmlExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx"); + + hr = pixd->loadXML(bstrLoad, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) + { + XmlReportParseError(pixpe); + } + + XmlExitOnFailure(hr, "failed loadXML"); + + + hr = S_OK; +LExit: + if (ppixdDocument) + { + *ppixdDocument = pixd; + pixd = NULL; + } + ReleaseBSTR(bstrLoad); + ReleaseObject(pixd); + ReleaseObject(pixpe); + + return hr; +} + + +/******************************************************************* + XmlLoadDocumentFromFile + +********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromFile( + __in_z LPCWSTR wzPath, + __out IXMLDOMDocument** ppixdDocument + ) +{ + return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument); +} + + +/******************************************************************* + XmlLoadDocumentFromFileEx + +********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx( + __in_z LPCWSTR wzPath, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + VARIANT varPath; + VARIANT_BOOL vbSuccess = 0; + + IXMLDOMDocument* pixd = NULL; + IXMLDOMParseError* pixpe = NULL; + + ::VariantInit(&varPath); + varPath.vt = VT_BSTR; + varPath.bstrVal = ::SysAllocString(wzPath); + XmlExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx"); + + hr = XmlCreateDocument(NULL, &pixd); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed XmlCreateDocument"); + + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) + { + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); + XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); + } + + // Avoid triggering anything external. + hr = pixd->put_validateOnParse(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixd->put_resolveExternals(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_resolveExternals"); + + pixd->put_async(VARIANT_FALSE); + hr = pixd->load(varPath, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) + { + XmlReportParseError(pixpe); + } + + XmlExitOnFailure(hr, "failed to load XML from: %ls", wzPath); + + if (ppixdDocument) + { + *ppixdDocument = pixd; + pixd = NULL; + } + + hr = S_OK; +LExit: + ReleaseVariant(varPath); + ReleaseObject(pixd); + ReleaseObject(pixpe); + + return hr; +} + + +/******************************************************************** + XmlLoadDocumentFromBuffer + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( + __in_bcount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + SAFEARRAY sa = { }; + VARIANT vtXmlSource; + VARIANT_BOOL vbSuccess = 0; + + ::VariantInit(&vtXmlSource); + + // create document + hr = XmlCreateDocument(NULL, &pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed XmlCreateDocument"); + + // Security issue. Avoid triggering anything external. + hr = pixdDocument->put_validateOnParse(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixdDocument->put_resolveExternals(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_resolveExternals"); + + // load document + sa.cDims = 1; + sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE; + sa.cbElements = 1; + sa.pvData = (PVOID)pbSource; + sa.rgsabound[0].cElements = (ULONG)cbSource; + vtXmlSource.vt = VT_ARRAY | VT_UI1; + vtXmlSource.parray = &sa; + + hr = pixdDocument->load(vtXmlSource, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + XmlExitOnFailure(hr, "failed loadXML"); + + // return value + *ppixdDocument = pixdDocument; + pixdDocument = NULL; + +LExit: + ReleaseObject(pixdDocument); + return hr; +} + + +/******************************************************************** + XmlSetAttribute - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in_z LPCWSTR pwzAttributeValue + ) +{ + HRESULT hr = S_OK; + VARIANT varAttributeValue; + ::VariantInit(&varAttributeValue); + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMAttribute* pixaAttribute = NULL; + IXMLDOMNode* pixaNode = NULL; + BSTR bstrAttributeName = ::SysAllocString(pwzAttribute); + XmlExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute"); + + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute); + + hr = pixnNode->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + + hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute); + XmlExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute); + + varAttributeValue.vt = VT_BSTR; + varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue); + if (!varAttributeValue.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + XmlExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute"); + + hr = pixaAttribute->put_nodeValue(varAttributeValue); + XmlExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute); + + hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode); + XmlExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute); + +LExit: + ReleaseObject(pixdDocument); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixaAttribute); + ReleaseObject(pixaNode); + ReleaseBSTR(varAttributeValue.bstrVal); + ReleaseBSTR(bstrAttributeName); + + return hr; +} + + +/******************************************************************** + XmlSelectSingleNode - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSelectSingleNode( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNode **ppixnChild + ) +{ + HRESULT hr = S_OK; + + BSTR bstrXPath = NULL; + + XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode"); + XmlExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode"); + + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); + XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode"); + + hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild); + +LExit: + ReleaseBSTR(bstrXPath); + + return hr; +} + + +/******************************************************************** + XmlCreateTextNode - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateTextNode( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzText, + __out IXMLDOMText **ppixnTextNode + ) +{ + if (!ppixnTextNode || !pixdDocument) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrText = ::SysAllocString(wzText); + XmlExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString"); + hr = pixdDocument->createTextNode(bstrText, ppixnTextNode); +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + + +/******************************************************************** + XmlGetText + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetText( + __in IXMLDOMNode* pixnNode, + __deref_out_z BSTR* pbstrText + ) +{ + return pixnNode->get_text(pbstrText); +} + + +/******************************************************************** + XmlGetAttribute + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __deref_out_z BSTR* pbstrAttributeValue + ) +{ + Assert(pixnNode); + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + VARIANT varAttributeValue; + BSTR bstrAttribute = SysAllocString(pwzAttribute); + + // INIT + ::VariantInit(&varAttributeValue); + + // get attribute value from source + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "failed get_attributes"); + + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); + if (S_FALSE == hr) + { + // hr = E_FAIL; + ExitFunction(); + } + XmlExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute); + + hr = pixnAttribute->get_nodeValue(&varAttributeValue); + XmlExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute); + + // steal the BSTR from the VARIANT + if (S_OK == hr && pbstrAttributeValue) + { + *pbstrAttributeValue = varAttributeValue.bstrVal; + varAttributeValue.bstrVal = NULL; + } + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + ReleaseVariant(varAttributeValue); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlGetAttributeEx + +*********************************************************************/ +HRESULT DAPI XmlGetAttributeEx( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __deref_out_z LPWSTR* psczAttributeValue + ) +{ + Assert(pixnNode); + HRESULT hr = S_OK; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + VARIANT varAttributeValue; + BSTR bstrAttribute = NULL; + + ::VariantInit(&varAttributeValue); + + // get attribute value from source + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "Failed get_attributes."); + + bstrAttribute = ::SysAllocString(wzAttribute); + XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR."); + + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + XmlExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute); + + hr = pixnAttribute->get_nodeValue(&varAttributeValue); + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + XmlExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute); + + // copy value + hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0); + XmlExitOnFailure(hr, "Failed to copy attribute value."); + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + ReleaseVariant(varAttributeValue); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlGetYesNoAttribute + +*********************************************************************/ +HRESULT DAPI XmlGetYesNoAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __out BOOL* pfYes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue); + if (E_NOTFOUND != hr) + { + XmlExitOnFailure(hr, "Failed to get attribute."); + + *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1); + } + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + + +/******************************************************************** + XmlGetAttributeNumber + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD* pdwValue + ) +{ + HRESULT hr = XmlGetAttributeNumberBase(pixnNode, pwzAttribute, 10, pdwValue); + return hr; +} + + +/******************************************************************** + XmlGetAttributeNumberBase + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeNumberBase( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in int nBase, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + BSTR bstrPointer = NULL; + + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer); + XmlExitOnFailure(hr, "Failed to get value from attribute."); + + if (S_OK == hr) + { + *pdwValue = wcstoul(bstrPointer, NULL, nBase); + } + +LExit: + ReleaseBSTR(bstrPointer); + return hr; +} + + +/******************************************************************** + XmlGetAttributeLargeNumber + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeLargeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD64* pdw64Value + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue); + XmlExitOnFailure(hr, "failed XmlGetAttribute"); + + if (S_OK == hr) + { + LONGLONG ll = 0; + hr = StrStringToInt64(bstrValue, 0, &ll); + XmlExitOnFailure(hr, "Failed to treat attribute value as number."); + + *pdw64Value = ll; + } + else + { + *pdw64Value = 0; + } + +LExit: + ReleaseBSTR(bstrValue); + return hr; +} + + +/******************************************************************** + XmlGetNamedItem - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetNamedItem( + __in IXMLDOMNamedNodeMap *pixnmAttributes, + __in_opt LPCWSTR wzName, + __out IXMLDOMNode **ppixnNamedItem + ) +{ + if (!pixnmAttributes || !ppixnNamedItem) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrName = ::SysAllocString(wzName); + XmlExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem); + +LExit: + ReleaseBSTR(bstrName); + return hr; +} + + +/******************************************************************** + XmlSetText - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetText( + __in IXMLDOMNode *pixnNode, + __in_z LPCWSTR pwzText + ) +{ + Assert(pixnNode && pwzText); + HRESULT hr = S_OK; + DOMNodeType dnType; + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNodeList* pixnlNodeList = NULL; + IXMLDOMNode* pixnChildNode = NULL; + IXMLDOMText* pixtTextNode = NULL; + VARIANT varText; + + ::VariantInit(&varText); + + // find the text node + hr = pixnNode->get_childNodes(&pixnlNodeList); + XmlExitOnFailure(hr, "failed to get child nodes"); + + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode))) + { + hr = pixnChildNode->get_nodeType(&dnType); + XmlExitOnFailure(hr, "failed to get node type"); + + if (NODE_TEXT == dnType) + break; + ReleaseNullObject(pixnChildNode); + } + if (S_FALSE == hr) + { + hr = S_OK; + } + + if (pixnChildNode) + { + varText.vt = VT_BSTR; + varText.bstrVal = ::SysAllocString(pwzText); + if (!varText.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + XmlExitOnFailure(hr, "failed SysAllocString in XmlSetText"); + + hr = pixnChildNode->put_nodeValue(varText); + XmlExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue"); + } + else + { + hr = pixnNode->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + + hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode); + XmlExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText); + + hr = pixnNode->appendChild(pixtTextNode, NULL); + XmlExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText); + } + + hr = *pwzText ? S_OK : S_FALSE; + +LExit: + ReleaseObject(pixnlNodeList); + ReleaseObject(pixnChildNode); + ReleaseObject(pixdDocument); + ReleaseObject(pixtTextNode); + ReleaseVariant(varText); + return hr; +} + + +/******************************************************************** + XmlSetTextNumber - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetTextNumber( + __in IXMLDOMNode *pixnNode, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + WCHAR wzValue[12]; + + hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue); + XmlExitOnFailure(hr, "Failed to format numeric value as string."); + + hr = XmlSetText(pixnNode, wzValue); + +LExit: + return hr; +} + + +/******************************************************************** + XmlCreateChild - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateChild( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR pwzElementType, + __out IXMLDOMNode** ppixnChild + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNode* pixnChild = NULL; + + hr = pixnParent->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed get_ownerDocument"); + + hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed createElement"); + + pixnParent->appendChild(pixnChild,NULL); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed appendChild"); + + if (ppixnChild) + { + *ppixnChild = pixnChild; + pixnChild = NULL; + } + +LExit: + ReleaseObject(pixdDocument); + ReleaseObject(pixnChild); + return hr; +} + +/******************************************************************** + XmlRemoveAttribute - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlRemoveAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + BSTR bstrAttribute = ::SysAllocString(pwzAttribute); + XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute"); + + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute); + + hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL); + XmlExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute); + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlSelectNodes - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSelectNodes( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNodeList **ppixnlChildren + ) +{ + HRESULT hr = S_OK; + + BSTR bstrXPath = NULL; + + XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes"); + XmlExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes"); + + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); + XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes"); + + hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren); + +LExit: + ReleaseBSTR(bstrXPath); + return hr; +} + + +/******************************************************************** + XmlNextAttribute - returns the next attribute in a node list + + NOTE: pbstrAttribute is optional + returns S_OK if found an element + returns S_FALSE if no element found + returns E_* if something went wrong +********************************************************************/ +extern "C" HRESULT DAPI XmlNextAttribute( + __in IXMLDOMNamedNodeMap* pixnnm, + __out IXMLDOMNode** pixnAttribute, + __deref_opt_out_z_opt BSTR* pbstrAttribute + ) +{ + Assert(pixnnm && pixnAttribute); + + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DOMNodeType nt; + + // null out the return values + *pixnAttribute = NULL; + if (pbstrAttribute) + { + *pbstrAttribute = NULL; + } + + hr = pixnnm->nextNode(&pixn); + XmlExitOnFailure(hr, "Failed to get next attribute."); + + if (S_OK == hr) + { + hr = pixn->get_nodeType(&nt); + XmlExitOnFailure(hr, "failed to get node type"); + + if (NODE_ATTRIBUTE != nt) + { + hr = E_UNEXPECTED; + XmlExitOnFailure(hr, "Failed to get expected node type back: attribute"); + } + + // if the caller asked for the attribute name + if (pbstrAttribute) + { + hr = pixn->get_baseName(pbstrAttribute); + XmlExitOnFailure(hr, "failed to get attribute name"); + } + + *pixnAttribute = pixn; + pixn = NULL; + } + +LExit: + ReleaseObject(pixn); + return hr; +} + + +/******************************************************************** + XmlNextElement - returns the next element in a node list + + NOTE: pbstrElement is optional + returns S_OK if found an element + returns S_FALSE if no element found + returns E_* if something went wrong +********************************************************************/ +extern "C" HRESULT DAPI XmlNextElement( + __in IXMLDOMNodeList* pixnl, + __out IXMLDOMNode** pixnElement, + __deref_opt_out_z_opt BSTR* pbstrElement + ) +{ + Assert(pixnl && pixnElement); + + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DOMNodeType nt; + + // null out the return values + *pixnElement = NULL; + if (pbstrElement) + { + *pbstrElement = NULL; + } + + // + // find the next element in the list + // + while (S_OK == (hr = pixnl->nextNode(&pixn))) + { + hr = pixn->get_nodeType(&nt); + XmlExitOnFailure(hr, "failed to get node type"); + + if (NODE_ELEMENT == nt) + break; + + ReleaseNullObject(pixn); + } + XmlExitOnFailure(hr, "failed to get next element"); + + // if we have a node and the caller asked for the element name + if (pixn && pbstrElement) + { + hr = pixn->get_baseName(pbstrElement); + XmlExitOnFailure(hr, "failed to get element name"); + } + + *pixnElement = pixn; + pixn = NULL; + + hr = *pixnElement ? S_OK : S_FALSE; +LExit: + ReleaseObject(pixn); + return hr; +} + + +/******************************************************************** + XmlRemoveChildren - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlRemoveChildren( + __in IXMLDOMNode* pixnSource, + __in_z LPCWSTR pwzXPath + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNodeList* pixnlNodeList = NULL; + IXMLDOMNode* pixnNode = NULL; + IXMLDOMNode* pixnRemoveChild = NULL; + + if (pwzXPath) + { + hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList); + XmlExitOnFailure(hr, "failed XmlSelectNodes"); + } + else + { + hr = pixnSource->get_childNodes(&pixnlNodeList); + XmlExitOnFailure(hr, "failed childNodes"); + } + if (S_FALSE == hr) + { + ExitFunction(); + } + + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode))) + { + hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild); + XmlExitOnFailure(hr, "failed removeChild"); + + ReleaseNullObject(pixnRemoveChild); + ReleaseNullObject(pixnNode); + } + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseObject(pixnlNodeList); + ReleaseObject(pixnNode); + ReleaseObject(pixnRemoveChild); + + return hr; +} + + +/******************************************************************** + XmlSaveDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSaveDocument( + __in IXMLDOMDocument* pixdDocument, + __inout LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + VARIANT varsDestPath; + + ::VariantInit(&varsDestPath); + varsDestPath.vt = VT_BSTR; + varsDestPath.bstrVal = ::SysAllocString(wzPath); + if (!varsDestPath.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + XmlExitOnFailure(hr, "failed to create BSTR"); + + hr = pixdDocument->save(varsDestPath); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed save in WriteDocument"); + +LExit: + ReleaseVariant(varsDestPath); + return hr; +} + + +/******************************************************************** + XmlSaveDocumentToBuffer + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSaveDocumentToBuffer( + __in IXMLDOMDocument* pixdDocument, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out DWORD* pcbDest + ) +{ + HRESULT hr = S_OK; + IStream* pStream = NULL; + LARGE_INTEGER li = { }; + STATSTG statstg = { }; + BYTE* pbDest = NULL; + ULONG cbRead = 0; + VARIANT vtDestination; + + ::VariantInit(&vtDestination); + + // create stream + hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream); + XmlExitOnFailure(hr, "Failed to create stream."); + + // write document to stream + vtDestination.vt = VT_UNKNOWN; + vtDestination.punkVal = (IUnknown*)pStream; + hr = pixdDocument->save(vtDestination); + XmlExitOnFailure(hr, "Failed to save document."); + + // get stream size + hr = pStream->Stat(&statstg, STATFLAG_NONAME); + XmlExitOnFailure(hr, "Failed to get stream size."); + + // allocate buffer + pbDest = static_cast(MemAlloc(statstg.cbSize.LowPart, TRUE)); + XmlExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); + + // read data from stream + li.QuadPart = 0; + hr = pStream->Seek(li, STREAM_SEEK_SET, NULL); + XmlExitOnFailure(hr, "Failed to seek stream."); + + hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead); + if (cbRead < statstg.cbSize.LowPart) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "Failed to read stream content to buffer."); + + // return value + *ppbDest = pbDest; + pbDest = NULL; + *pcbDest = statstg.cbSize.LowPart; + +LExit: + ReleaseObject(pStream); + ReleaseMem(pbDest); + return hr; +} 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 @@ + + + + + + + + Schema for describing Theme files processed by thmutil. + + + + + + + + + This is the top-level container element for every thmutil Theme file. + + + + + + + + + + + Relative path to an image file that can serve as a single source for images in the rest of the theme. + This image is referenced by controls using the SourceX and SourceY attributes. + Mutually exclusive with the ImageResource attribute. + + + + + + + Identifier that references an image resource in the module for the window. + Mutually exclusive with the ImageFile attribute. + + + + + + + + + Defines a font including the size and color. + + + + + + Name of the font face (required). + + + + 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. + + + + + Font size. Use negative numbers to specify the font in pixels. + + + + + Font weight. + + + + + + A system color id or a hexadecimal value representing BGR foreground color of the font. + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. + If this attribute is absent the foreground will be transparent. + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. + + + + + + + A system color id or a hexadecimal value representing BGR background color of the font. + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. + If this attribute is absent the background will be transparent. + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. + + + + + + Specifies whether the font is underlined. + + + + + + + + + + List of images which can be shared between multiple controls. + + + + + + + + + Name of the ImageList, to be referenced by other controls. + + + + + + + + + Named set of controls that can be shown and hidden collectively. + + + + + + + Optional name for the page. + + + + + + + + + Defines the overall look of the main window. + + + + + + + + + + Specifies whether the ThmUtil default window proc should process WM_SIZE and WM_SIZING events. + + + + + + Caption for the window. + This is required if not using the StringId attribute. + + + + + + Numeric identifier to the Font element that serves as the default font for the window. + + + + + Height of the window's client area. + + + + + + Hexadecimal window style. If this is not specified the default value is: WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU. + If SourceX and SourceY are specified, then WS_OVERLAPPED is replaced with WS_POPUP. + + + + + + Relative path to an icon file for the window. Mutually exclusive with IconResource and SourceX and SourceY attributes. + + + + + + Identifier that references an icon resource in the module for the icon for the window. + Mutually exclusive with IconFile and SourceX and SourceY attributes. + + + + + + Minimum height of the window. Only functions if AutoResize is enabled. + + + + + Minimum width of the window. Only functions if AutoResize is enabled. + + + + + X offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. + + + + + Y offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. + + + + + + Identifier that references a string resource in the module to define the window caption. + Mutually exclusive with the Caption attribute. + + + + + + Width of the window's client area. + + + + + + + + Defines a control that rotates through a set of images on a specified interval. + + + + + + + + + + Specifies the time to wait before showing the next image, in milliseconds. + + + + + + Specifies whether the billboard should loop through the images infinitely. + + + + + + + + Defines a button. + + + + + Text to display in the button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + 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). + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. + + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. + + + + + Numeric identifier to the Font element that serves as the font when the control is hovered over. Only valid when using graphic buttons. + + + + + + Relative path to an image file to define a graphic button. + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an image resource in the module to define a graphic button. + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + Numeric identifier to the Font element that serves as the font when the control is selected. Only valid when using graphic buttons. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + + When the button is pressed, a directory browser dialog is shown. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + The name of the variable to update when the user selects a directory from the dialog. + + + + + + + + + + When the button is pressed, the specified page is shown. + + + + + + + When set to 'yes', none of the variable changes made on the current page are saved. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + The Name of the Page to show. + + + + + + + + + + When the button is pressed, the WM_CLOSE message is sent to the window. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + + + Defines a checkbox. + + + + + Text to display beside the checkbox. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines a combobox. + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + + Defines a button. + + + + + Text to display in the button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + 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). + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. + + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. + + + + + + Relative path to an icon file to define a command link glyph. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an icon resource in the module to define a command link glyph. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + Relative path to an image file to define a command link glyph. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an image resource in the module to define a command link glyph. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines an edit box. + + + + + + + Initial text for the control. + Mutually exclusive with the StringId attribute. + + + + + + Specifies whether the edit box should auto-complete with file system paths. + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the initial text for the control. + + + + + + + + + + + Defines a hyperlink. + + + + + Text to display as the link. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the unselected font. + + + + + Numeric identifier to the Font element that serves as the font when the control is hovered over. + + + + + Numeric identifier to the Font element that serves as the font when the control is selected. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines a text block with support for HTML <a> tags. + + + + + Text to display as the link. + Use HTML <a href="URL"> to create a link. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines an image for an ImageList or Billboard. + + + + + Relative path to an image file. Mutually exclusive with ImageResource. + + + + + Identifier that references an image resource in the module. Mutually exclusive with ImageFile. + + + + + + + + Defines an image. + + + + + + Relative path to an image file. Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + Identifier that references an image resource in the module. Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + + Defines a label. + + + + + Text for the label to display. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Specifies whether the text should be centered horizontally in the width of the control. Default is "no". + + + + + By default ampersands (&) 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". + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the label. + + + + + + + + + Defines a listview. + + + + + + + + + Numeric identifier to the Font element that serves as the default font for the ListView. + + + + + Hexadecimal extended window style. + + + + + + The name of the ImageList to assign to this listview with type LVSIL_NORMAL. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_SMALL. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_STATE. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_GROUPHEADER. + + + + + + + + + + Defines note text for a command link control based on an optional condition. + 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). + 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. + + + + + + + + Note text for the parent command link control. + + + + + + The condition that determines when the parent control will use this note text. + + + + + + + + + + + Defines a collection of controls. + + + + + + + + + + Defines a progress bar. + + + + + + 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. + + + + + 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. + + + + + + + + Defines an individual radio button within a set of radio buttons. + + + + + Text to display beside the radio button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + Optional value used when setting the variable associated with the set of radio buttons. + + + + + + + + Defines a set of radio buttons. + + + + + + + + Optional variable name for the set of radio buttons. + + + + + + + + Defines a rich edit control. + + + + + Initial text for the control. + Mutually exclusive with the StringId attribute. + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + Identifier that references a string resource in the module to define the initial text for the control. + + + + + + + + + Defines a straight line. + + + + + + + + + Defines an individual tab within a set of tabs. + + + + + + + Caption of the tab. + Mutually exclusive with the StringId attribute. + + + + + + Identifier that references a string resource in the module to define the caption of the tab. + + + + + + + + + + + Defines a set of tabs. + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + + + Defines text for the parent control based on an optional condition. + 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). + 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. + + + + + + + + Text for the parent control. + + + + + + The condition that determines when the parent control will use this text. + + + + + + + + + + + + Defines text for the parent control's tooltip. + + + + + + + + Text for the parent control's tooltip. + + + + + + + + + + Defines a treeview. + + + + + + Specifies whether the row always appears selected even when the treeview has lost focus. + + + + + Specifies whether drag and drop is enabled for the treeview. + + + + + Specifies whether an entire row is selected for the treeview. + + + + + Specifies whether the treeview will show buttons. + + + + + Specifies whether lines appear for all treeview items. + + + + + Specifies whether the root nodes have lines beside them. + + + + + + + + A column of a list. + + + + + + + Text for the column header. + Mutually exclusive with the StringId attribute. + + + + + Width of the column. + + + + + + Whether or not this column can grow to fill available width of the listview. + More than one column can be marked with yes - all expandable columns will share available extra space. + This is especially useful if the Window/@AutoResize is yes. + + + + + + + Identifier that references a string resource in the module to define the text for the column header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Optional name for the control. + + + + + Set to 'yes' to disable automatic variable getting and setting, EnableCondition, VisibleCondition, and conditional Text elements. The default is 'no'. + + + + + A condition that determines if the control is enabled. If this condition is true or omitted, then the control will be enabled. + + + + + Height of the control. Non-positive values extend the control to the bottom of the window minus the value. + + + + + Hexadecimal window style for the control. + + + + + Specifies whether the control should be hidden when disabled. + + + + + Specifies whether the control is part of the tab sequence of controls. + + + + + Specifies whether the control is initially visible. + + + + + + A condition that determines if the control is visible. If this condition is true or omitted, then the control will be visible. + + + + + + Width of the control. Non-positive values extend the control to the right of the window minus the value. + + + + + 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. + + + + + 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. + + + + + + + Values of this type will either be "yes" or "no". + + + + + + + + + + + Indicates a system color for a font. + + + + + + + + + + + + + + + + + + Indicates the foreground or background color of a font. + + + + + 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 @@ +@setlocal +@pushd %~dp0 +@set _C=Release +@if /i "%1"=="debug" set _C=Debug + +nuget restore || exit /b + +msbuild -t:Test -p:Configuration=%_C% src\test\DUtilUnitTest || exit /b + +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v140 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v140 || exit /b + +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v141 || exit /b + +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v142 || exit /b + +msbuild -p:Configuration=%_C% -t:PackNative src\dutil\dutil.vcxproj || exit /b + +@popd +@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 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + +branches: + only: + - master + - develop + +image: Visual Studio 2019 + +version: 0.0.0.{build} +configuration: Release + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +build_script: + - appveyor.cmd + +test: off + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_branch_with_pr: true +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget +- path: build\Release\**\*.msi + name: msi + +notifications: +- provider: Slack + incoming_webhook: + 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 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DUtilUnitTest", "src\test\DUtilUnitTest\DUtilUnitTest.vcxproj", "{AB7EE608-E5FB-42A5-831F-0DEEEA141223}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.Build.0 = Debug|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.ActiveCfg = Release|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.Build.0 = Release|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM64.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x64.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.Build.0 = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM64.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x64.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133} + EndGlobalSection +EndGlobal 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 @@ + + + + + + + + \ 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class ApupUtil + { + public: + [Fact] + void AllocChainFromAtomSortsDescending() + { + HRESULT hr = S_OK; + ATOM_FEED* pFeed = NULL; + APPLICATION_UPDATE_CHAIN* pChain = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + XmlInitialize(); + NativeAssert::Succeeded(hr, "Failed to initialize Xml."); + + pin_ptr feedFilePath = PtrToStringChars(TestData::Get("TestData", "ApupUtilTests", "FeedBv2.0.xml")); + hr = AtomParseFromFile(feedFilePath, &pFeed); + NativeAssert::Succeeded(hr, "Failed to parse feed: {0}", feedFilePath); + + hr = ApupAllocChainFromAtom(pFeed, &pChain); + NativeAssert::Succeeded(hr, "Failed to get chain from feed."); + + Assert::Equal(3ul, pChain->cEntries); + NativeAssert::StringEqual(L"Bundle v2.0", pChain->rgEntries[0].wzTitle); + NativeAssert::StringEqual(L"Bundle v1.0", pChain->rgEntries[1].wzTitle); + NativeAssert::StringEqual(L"Bundle v1.0-preview", pChain->rgEntries[2].wzTitle); + } + finally + { + DutilUninitialize(); + } + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; + +[assembly: AssemblyTitleAttribute("Windows Installer XML Dutil unit tests")]; +[assembly: AssemblyDescriptionAttribute("Dutil unit tests")]; +[assembly: AssemblyCultureAttribute("")]; +[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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class DUtil + { + public: + [Fact] + void DUtilTraceErrorSourceFiltersOnTraceLevel() + { + DutilInitialize(&DutilTestTraceError); + + CallDutilTraceErrorSource(); + + Dutil_TraceSetLevel(REPORT_DEBUG, FALSE); + + Action^ action = gcnew Action(this, &DUtil::CallDutilTraceErrorSource); + Assert::Throws(action); + + DutilUninitialize(); + } + + private: + void CallDutilTraceErrorSource() + { + Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, DUTIL_SOURCE_EXTERNAL, E_FAIL, "Error message"); + } + }; +} 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 @@ + + + + + + + + Debug + Win32 + + + Release + Win32 + + + + + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + {AB7EE608-E5FB-42A5-831F-0DEEEA141223} + DUtilUnitTests + ManagedCProj + DynamicLibrary + Unicode + true + false + + + + + + + ..\..\dutil\inc + rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib + + + + + + + + + + + + + + + + + Create + + 4564;4691 + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll + + + + + + {1244E671-F108-4334-BA52-8A7517F26ECD} + + + + + + + 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}. + + + + + \ 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 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Header Files + + + Header Files + + + \ 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +const DWORD numIterations = 100000; + +namespace DutilTests +{ + struct Value + { + DWORD dwNum; + LPWSTR sczKey; + }; + + public ref class DictUtil + { + public: + [Fact] + void DictUtilTest() + { + DutilInitialize(&DutilTestTraceError); + + EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations); + + EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); + + StringListTestHelper(DICT_FLAG_NONE, numIterations); + + StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); + + DutilUninitialize(); + } + + private: + void EmbeddedKeyTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) + { + HRESULT hr = S_OK; + Value *rgValues = NULL; + Value *valueFound = NULL; + DWORD cValues = 0; + LPWSTR sczExpectedKey = NULL; + STRINGDICT_HANDLE sdValues = NULL; + + try + { + hr = DictCreateWithEmbeddedKey(&sdValues, 0, (void **)&rgValues, offsetof(Value, sczKey), dfFlags); + NativeAssert::Succeeded(hr, "Failed to create dictionary of values"); + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + cValues++; + + hr = MemEnsureArraySize((void **)&rgValues, cValues, sizeof(Value), 5); + NativeAssert::Succeeded(hr, "Failed to grow value array"); + + hr = StrAllocFormatted(&rgValues[i].sczKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); + + hr = DictAddValue(sdValues, rgValues + i); + NativeAssert::Succeeded(hr, "Failed to add item {0} to dict", i); + } + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey); + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + + if (dfFlags & DICT_FLAG_CASEINSENSITIVE) + { + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey, TRUE); + } + else + { + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "This embedded key is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); + } + } + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); + } + } + } + finally + { + for (DWORD i = 0; i < cValues; ++i) + { + ReleaseStr(rgValues[i].sczKey); + } + ReleaseMem(rgValues); + ReleaseStr(sczExpectedKey); + ReleaseDict(sdValues); + } + + LExit: + return; + } + + void StringListTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) + { + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + LPWSTR sczExpectedKey = NULL; + STRINGDICT_HANDLE sdValues = NULL; + + try + { + hr = DictCreateStringList(&sdValues, 0, dfFlags); + NativeAssert::Succeeded(hr, "Failed to create dictionary of keys"); + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); + + hr = DictAddKey(sdValues, sczKey); + NativeAssert::Succeeded(hr, "Failed to add key {0} to dict", i); + } + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + if (dfFlags & DICT_FLAG_CASEINSENSITIVE) + { + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + } + else + { + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "This stringlist dict is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); + } + } + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); + } + } + } + finally + { + ReleaseStr(sczKey); + ReleaseStr(sczExpectedKey); + ReleaseDict(sdValues); + } + + LExit: + return; + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class DirUtil + { + public: + [Fact] + void DirUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczCurrentDir = NULL; + LPWSTR sczGuid = NULL; + LPWSTR sczFolder = NULL; + LPWSTR sczSubFolder = NULL; + + try + { + hr = GuidCreate(&sczGuid); + NativeAssert::Succeeded(hr, "Failed to create guid."); + + hr = DirGetCurrent(&sczCurrentDir); + NativeAssert::Succeeded(hr, "Failed to get current directory."); + + hr = PathConcat(sczCurrentDir, sczGuid, &sczFolder); + NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid); + + BOOL fExists = DirExists(sczFolder, NULL); + Assert::False(fExists == TRUE); + + hr = PathConcat(sczFolder, L"foo", &sczSubFolder); + NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder); + + hr = DirEnsureExists(sczSubFolder, NULL); + NativeAssert::Succeeded(hr, "Failed to create multiple directories: %ls", sczSubFolder); + + // Test failure to delete non-empty folder. + hr = DirEnsureDelete(sczFolder, FALSE, FALSE); + Assert::Equal(HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY), hr); + + hr = DirEnsureDelete(sczSubFolder, FALSE, FALSE); + NativeAssert::Succeeded(hr, "Failed to delete single directory: %ls", sczSubFolder); + + // Put the directory back and we'll test deleting tree. + hr = DirEnsureExists(sczSubFolder, NULL); + NativeAssert::Succeeded(hr, "Failed to create single directory: %ls", sczSubFolder); + + hr = DirEnsureDelete(sczFolder, FALSE, TRUE); + NativeAssert::Succeeded(hr, "Failed to delete directory tree: %ls", sczFolder); + + // Finally, try to create "C:\" which would normally fail, but we want success + hr = DirEnsureExists(L"C:\\", NULL); + NativeAssert::Succeeded(hr, "Failed to create C:\\"); + } + finally + { + ReleaseStr(sczSubFolder); + ReleaseStr(sczFolder); + ReleaseStr(sczGuid); + ReleaseStr(sczCurrentDir); + } + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class FileUtil + { + public: + [Fact(Skip="Skipped until we have a good way to reference ANSI.txt.")] + void FileUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczTempDir = NULL; + LPWSTR sczFileDir = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get temp dir"); + + hr = PathExpand(&sczFileDir, L"%WIX_ROOT%\\examples\\data\\TextEncodings\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to encodings file dir"); + + hr = DirEnsureExists(sczTempDir, NULL); + NativeAssert::Succeeded(hr, "Failed to ensure directory exists: {0}", sczTempDir); + + TestFile(sczFileDir, sczTempDir, L"ANSI.txt", 32, FILE_ENCODING_UTF8); + // Big endian not supported today! + //TestFile(sczFileDir, L"UnicodeBENoBOM.txt", 34); + //TestFile(sczFileDir, L"UnicodeBEWithBOM.txt", 34); + TestFile(sczFileDir, sczTempDir, L"UnicodeLENoBOM.txt", 34, FILE_ENCODING_UTF16); + TestFile(sczFileDir, sczTempDir, L"UnicodeLEWithBOM.txt", 34, FILE_ENCODING_UTF16_WITH_BOM); + TestFile(sczFileDir, sczTempDir, L"UTF8WithSignature.txt", 34, FILE_ENCODING_UTF8_WITH_BOM); + + hr = DirEnsureDelete(sczTempDir, TRUE, TRUE); + } + finally + { + ReleaseStr(sczTempDir); + ReleaseStr(sczFileDir); + DutilUninitialize(); + } + } + + private: + void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, size_t cbExpectedStringLength, FILE_ENCODING feExpectedEncoding) + { + HRESULT hr = S_OK; + LPWSTR sczFullPath = NULL; + LPWSTR sczContents = NULL; + LPWSTR sczOutputPath = NULL; + FILE_ENCODING feEncodingFound = FILE_ENCODING_UNSPECIFIED; + BYTE *pbFile1 = NULL; + DWORD cbFile1 = 0; + BYTE *pbFile2 = NULL; + DWORD cbFile2 = 0; + size_t cbActualStringLength = 0; + + try + { + hr = PathConcat(wzDir, wzFileName, &sczFullPath); + NativeAssert::Succeeded(hr, "Failed to create path to test file: {0}", sczFullPath); + + hr = FileToString(sczFullPath, &sczContents, &feEncodingFound); + hr = E_FAIL; + NativeAssert::Succeeded(hr, "Failed to read text from file: {0}", sczFullPath); + + if (!sczContents) + { + hr = E_FAIL; + NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath); + } + + hr = ::StringCchLengthW(sczContents, STRSAFE_MAX_CCH, &cbActualStringLength); + NativeAssert::Succeeded(hr, "Failed to get length of text from file: {0}", sczFullPath); + + if (cbActualStringLength != cbExpectedStringLength) + { + hr = E_FAIL; + ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %Iu, found size %Iu)", sczFullPath, cbExpectedStringLength, cbActualStringLength); + } + + if (feEncodingFound != feExpectedEncoding) + { + hr = E_FAIL; + ExitOnFailure(hr, "FileToString() returned unexpected encoding type for file: %ls (expected type %u, found type %u)", sczFullPath, feExpectedEncoding, feEncodingFound); + } + + hr = PathConcat(wzTempDir, wzFileName, &sczOutputPath); + NativeAssert::Succeeded(hr, "Failed to get output path"); + + hr = FileFromString(sczOutputPath, 0, sczContents, feExpectedEncoding); + NativeAssert::Succeeded(hr, "Failed to write contents of file back out to disk"); + + hr = FileRead(&pbFile1, &cbFile1, sczFullPath); + NativeAssert::Succeeded(hr, "Failed to read input file as binary"); + + hr = FileRead(&pbFile2, &cbFile2, sczOutputPath); + NativeAssert::Succeeded(hr, "Failed to read output file as binary"); + + if (cbFile1 != cbFile2 || 0 != memcmp(pbFile1, pbFile2, cbFile1)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Outputted file doesn't match input file: \"%ls\" and \"%ls\"", sczFullPath, sczOutputPath); + } + } + finally + { + ReleaseStr(sczOutputPath); + ReleaseStr(sczFullPath); + ReleaseStr(sczContents); + } + + LExit: + return; + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class GuidUtil + { + public: + [Fact] + void GuidCreateTest() + { + HRESULT hr = S_OK; + WCHAR wzGuid1[GUID_STRING_LENGTH]; + WCHAR wzGuid2[GUID_STRING_LENGTH]; + + hr = GuidFixedCreate(wzGuid1); + NativeAssert::Succeeded(hr, "Failed to create first guid."); + Guid firstGuid = Guid::Parse(gcnew String(wzGuid1)); + + hr = GuidFixedCreate(wzGuid2); + NativeAssert::Succeeded(hr, "Failed to create second guid."); + Guid secondGuid = Guid::Parse(gcnew String(wzGuid2)); + + NativeAssert::NotStringEqual(wzGuid1, wzGuid2); + NativeAssert::NotEqual(firstGuid, secondGuid); + } + + [Fact] + void GuidCreateSczTest() + { + HRESULT hr = S_OK; + LPWSTR sczGuid1 = NULL; + LPWSTR sczGuid2 = NULL; + + try + { + hr = GuidCreate(&sczGuid1); + NativeAssert::Succeeded(hr, "Failed to create first guid."); + Guid firstGuid = Guid::Parse(gcnew String(sczGuid1)); + + hr = GuidCreate(&sczGuid2); + NativeAssert::Succeeded(hr, "Failed to create second guid."); + Guid secondGuid = Guid::Parse(gcnew String(sczGuid2)); + + NativeAssert::NotStringEqual(sczGuid1, sczGuid2); + NativeAssert::NotEqual(firstGuid, secondGuid); + } + finally + { + ReleaseStr(sczGuid1); + ReleaseStr(sczGuid2); + } + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +typedef HRESULT (__clrcall *IniFormatParameters)( + INI_HANDLE + ); + +namespace DutilTests +{ + public ref class IniUtil + { + public: + [Fact] + void IniUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczTempIniFilePath = NULL; + LPWSTR sczTempIniFileDir = NULL; + 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"; + 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"; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to temp INI file"); + + hr = PathGetDirectory(sczTempIniFilePath, &sczTempIniFileDir); + NativeAssert::Succeeded(hr, "Failed to get directory to temp INI file"); + + hr = DirEnsureDelete(sczTempIniFileDir, TRUE, TRUE); + if (E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + NativeAssert::Succeeded(hr, "Failed to delete IniUtilTest directory: {0}", sczTempIniFileDir); + + hr = DirEnsureExists(sczTempIniFileDir, NULL); + NativeAssert::Succeeded(hr, "Failed to ensure temp directory exists: {0}", sczTempIniFileDir); + + // Tests parsing, then modifying a regular INI file + TestReadThenWrite(sczTempIniFilePath, StandardIniFormat, wzIniContents); + + // Tests programmatically creating from scratch, then parsing an INI file + TestWriteThenRead(sczTempIniFilePath, StandardIniFormat); + + // Tests parsing, then modifying a regular INI file + TestReadThenWrite(sczTempIniFilePath, ScriptFormat, wzScriptContents); + + // Tests programmatically creating from scratch, then parsing an INI file + TestWriteThenRead(sczTempIniFilePath, ScriptFormat); + } + finally + { + ReleaseStr(sczTempIniFilePath); + ReleaseStr(sczTempIniFileDir); + DutilUninitialize(); + } + } + + private: + void AssertValue(INI_HANDLE iniHandle, LPCWSTR wzValueName, LPCWSTR wzValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + try + { + hr = IniGetValue(iniHandle, wzValueName, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get ini value: {0}", wzValueName); + + if (0 != wcscmp(sczValue, wzValue)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Expected to find value in INI: '%ls'='%ls' - but found value '%ls' instead", wzValueName, wzValue, sczValue); + } + } + finally + { + ReleaseStr(sczValue); + } + + LExit: + return; + } + + void AssertNoValue(INI_HANDLE iniHandle, LPCWSTR wzValueName) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + try + { + hr = IniGetValue(iniHandle, wzValueName, &sczValue); + if (E_NOTFOUND != hr) + { + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "INI value shouldn't have been found: %ls", wzValueName); + } + } + finally + { + ReleaseStr(sczValue); + } + + LExit: + return; + } + + static HRESULT StandardIniFormat(__inout INI_HANDLE iniHandle) + { + HRESULT hr = S_OK; + + hr = IniSetOpenTag(iniHandle, L"[", L"]"); + NativeAssert::Succeeded(hr, "Failed to set open tag settings on ini handle"); + + hr = IniSetValueStyle(iniHandle, NULL, L"="); + NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); + + hr = IniSetCommentStyle(iniHandle, L";"); + NativeAssert::Succeeded(hr, "Failed to set comment style setting on ini handle"); + + return hr; + } + + static HRESULT ScriptFormat(__inout INI_HANDLE iniHandle) + { + HRESULT hr = S_OK; + + hr = IniSetValueStyle(iniHandle, L"setf ~", L" "); + NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); + + return hr; + } + + void TestReadThenWrite(LPWSTR wzIniFilePath, IniFormatParameters SetFormat, LPCWSTR wzContents) + { + HRESULT hr = S_OK; + INI_HANDLE iniHandle = NULL; + INI_HANDLE iniHandle2 = NULL; + INI_VALUE *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = FileWrite(wzIniFilePath, 0, reinterpret_cast(wzContents), lstrlenW(wzContents) * sizeof(WCHAR), NULL); + NativeAssert::Succeeded(hr, "Failed to write out INI file"); + + hr = IniInitialize(&iniHandle); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(5, cValues); + + AssertValue(iniHandle, L"PlainValue", L"Blah"); + AssertNoValue(iniHandle, L"PlainValue2"); + AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); + AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); + AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + + hr = IniSetValue(iniHandle, L"PlainValue2", L"Blah2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(7, cValues); + + AssertValue(iniHandle, L"PlainValue", L"Blah"); + AssertValue(iniHandle, L"PlainValue2", L"Blah2"); + AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); + AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); + AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); + + // Try deleting a value as well + hr = IniSetValue(iniHandle, L"Section1\\Section1ValueB", NULL); + NativeAssert::Succeeded(hr, "Failed to kill value in INI"); + + hr = IniWriteFile(iniHandle, NULL, FILE_ENCODING_UNSPECIFIED); + NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); + + ReleaseNullIni(iniHandle); + // Now re-parse the INI we just wrote and make sure it matches the values we expect + hr = IniInitialize(&iniHandle2); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle2); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle2, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle2, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(6, cValues); + + AssertValue(iniHandle2, L"PlainValue", L"Blah"); + AssertValue(iniHandle2, L"PlainValue2", L"Blah2"); + AssertValue(iniHandle2, L"Section1\\Section1ValueA", L"Foo"); + AssertNoValue(iniHandle2, L"Section1\\Section1ValueB"); + AssertValue(iniHandle2, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle2, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle2, L"Section1\\CreatedValue", L"Woo"); + AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arrmod"); + } + finally + { + ReleaseIni(iniHandle); + ReleaseIni(iniHandle2); + } + } + + void TestWriteThenRead(LPWSTR wzIniFilePath, IniFormatParameters SetFormat) + { + HRESULT hr = S_OK; + INI_HANDLE iniHandle = NULL; + INI_HANDLE iniHandle2 = NULL; + INI_VALUE *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = FileEnsureDelete(wzIniFilePath); + NativeAssert::Succeeded(hr, "Failed to ensure file is deleted"); + + hr = IniInitialize(&iniHandle); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(0, cValues); + + hr = IniSetValue(iniHandle, L"Value1", L"BlahTypo"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value2", L"Blah2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value3", L"Blah3"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value4", L"Blah4"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value4", NULL); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value1", L"Blah1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(8, cValues); + + AssertValue(iniHandle, L"Value1", L"Blah1"); + AssertValue(iniHandle, L"Value2", L"Blah2"); + AssertValue(iniHandle, L"Value3", L"Blah3"); + AssertNoValue(iniHandle, L"Value4"); + AssertValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); + AssertValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); + AssertValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + + hr = IniWriteFile(iniHandle, wzIniFilePath, FILE_ENCODING_UNSPECIFIED); + NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); + + ReleaseNullIni(iniHandle); + // Now re-parse the INI we just wrote and make sure it matches the values we expect + hr = IniInitialize(&iniHandle2); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle2); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle2, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle2, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(7, cValues); + + AssertValue(iniHandle2, L"Value1", L"Blah1"); + AssertValue(iniHandle2, L"Value2", L"Blah2"); + AssertValue(iniHandle2, L"Value3", L"Blah3"); + AssertNoValue(iniHandle2, L"Value4"); + AssertValue(iniHandle2, L"Section1\\Value1", L"Section1Value1"); + AssertValue(iniHandle2, L"Section1\\Value2", L"Section1Value2"); + AssertValue(iniHandle2, L"Section2\\Value1", L"Section2Value1"); + AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arr"); + } + finally + { + ReleaseIni(iniHandle); + ReleaseIni(iniHandle2); + } + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + struct ArrayValue + { + DWORD dwNum; + void *pvNull1; + LPWSTR sczString; + void *pvNull2; + }; + + public ref class MemUtil + { + public: + [Fact] + void MemUtilAppendTest() + { + HRESULT hr = S_OK; + DWORD dwSize; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 1"); + ++cValues; + SetItem(rgValues + 0, 0); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 2"); + ++cValues; + SetItem(rgValues + 1, 1); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 3"); + ++cValues; + SetItem(rgValues + 2, 2); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 4"); + ++cValues; + SetItem(rgValues + 3, 3); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 5"); + ++cValues; + SetItem(rgValues + 4, 4); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 6"); + ++cValues; + SetItem(rgValues + 5, 5); + + // OK, we used growth size 5, so let's try ensuring we have space for 6 (5 + first item) items + // and make sure it doesn't grow since we already have enough space + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to ensure array size matches what it should already be"); + dwSize = MemSize(rgValues); + if (dwSize != 6 * sizeof(ArrayValue)) + { + hr = E_FAIL; + ExitOnFailure(hr, "MemEnsureArraySize is growing an array that is already big enough!"); + } + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 6, 6); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 7, 7); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 8, 8); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + } + + LExit: + DutilUninitialize(); + } + + [Fact] + void MemUtilInsertTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of empty array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 5); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert at end of array"); + ++cValues; + CheckNullItem(rgValues + 1); + SetItem(rgValues + 1, 6); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 4); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 3); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 1); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 1); + SetItem(rgValues + 1, 2); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 0); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + CheckNullItem(rgValues + 7); + SetItem(rgValues + 7, 7); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 8, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 8); + SetItem(rgValues + 8, 8); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + [Fact] + void MemUtilRemovePreserveOrderTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Remove last item + MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), TRUE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove last two items + MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove first item + MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), TRUE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i + 1); + } + + + // Remove first two items + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i + 3); + } + + // Remove middle two items + MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + CheckItem(rgValues, 3); + CheckItem(rgValues + 1, 6); + + // Remove last 2 items to ensure we don't crash + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + [Fact] + void MemUtilRemoveFastTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Remove last item + MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), FALSE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove last two items + MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove first item + MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), FALSE); + --cValues; + + CheckItem(rgValues, 6); + CheckItem(rgValues + 1, 1); + CheckItem(rgValues + 2, 2); + CheckItem(rgValues + 3, 3); + CheckItem(rgValues + 4, 4); + CheckItem(rgValues + 5, 5); + + // Remove first two items + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + CheckItem(rgValues, 4); + CheckItem(rgValues + 1, 5); + CheckItem(rgValues + 2, 2); + CheckItem(rgValues + 3, 3); + + + // Remove middle two items + MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + CheckItem(rgValues, 4); + CheckItem(rgValues + 1, 3); + + // Remove last 2 items to ensure we don't crash + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + [Fact] + void MemUtilSwapTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Swap first two + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 1); + CheckItem(rgValues + 1, 0); + for (DWORD i = 2; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap them back + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap first and last items (index 0 and 9) + MemArraySwapItems(rgValues, 0, 9, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 9, 0); + for (DWORD i = 1; i < cValues - 1; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 1 and 8 + MemArraySwapItems(rgValues, 1, 8, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 1, 8); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 2; i < cValues - 2; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 2 and 7 + MemArraySwapItems(rgValues, 2, 7, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 1, 8); + CheckItem(rgValues + 2, 7); + CheckItem(rgValues + 7, 2); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 3; i < cValues - 3; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 0 and 1 + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 8); + CheckItem(rgValues + 1, 9); + CheckItem(rgValues + 2, 7); + CheckItem(rgValues + 7, 2); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 3; i < cValues - 3; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + private: + void SetItem(ArrayValue *pValue, DWORD dwValue) + { + HRESULT hr = S_OK; + pValue->dwNum = dwValue; + + hr = StrAllocFormatted(&pValue->sczString, L"%u", dwValue); + NativeAssert::Succeeded(hr, "Failed to allocate string"); + } + + void CheckItem(ArrayValue *pValue, DWORD dwValue) + { + HRESULT hr = S_OK; + LPWSTR sczTemp = NULL; + + try + { + NativeAssert::Equal(dwValue, pValue->dwNum); + + hr = StrAllocFormatted(&sczTemp, L"%u", dwValue); + NativeAssert::Succeeded(hr, "Failed to allocate temp string"); + + NativeAssert::StringEqual(sczTemp, pValue->sczString, TRUE); + + if (pValue->pvNull1 || pValue->pvNull2) + { + hr = E_FAIL; + ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); + } + } + finally + { + ReleaseStr(sczTemp); + } + + LExit: + return; + } + + void CheckNullItem(ArrayValue *pValue) + { + HRESULT hr = S_OK; + + NativeAssert::Equal(0, pValue->dwNum); + + if (pValue->sczString) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item found isn't NULL!"); + } + + if (pValue->pvNull1 || pValue->pvNull2) + { + hr = E_FAIL; + ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); + } + + LExit: + return; + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#undef RemoveDirectory + +using namespace System; +using namespace System::Collections::Generic; +using namespace System::Runtime::InteropServices; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + const int PREWAIT = 20; + const int POSTWAIT = 480; + const int FULLWAIT = 500; + const int SILENCEPERIOD = 100; + + struct RegKey + { + HRESULT hr; + HKEY hkRoot; + LPCWSTR wzSubKey; + REG_KEY_BITNESS kbKeyBitness; + BOOL fRecursive; + }; + struct Directory + { + HRESULT hr; + LPCWSTR wzPath; + BOOL fRecursive; + }; + struct Results + { + RegKey *rgRegKeys; + DWORD cRegKeys; + Directory *rgDirectories; + DWORD cDirectories; + }; + + public delegate void MonGeneralDelegate(HRESULT, LPVOID); + + public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID); + + public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID); + + public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID); + + static void MonGeneral( + __in HRESULT /*hrResult*/, + __in_opt LPVOID /*pvContext*/ + ) + { + Assert::True(false); + } + + static void MonDriveStatus( + __in WCHAR /*chDrive*/, + __in BOOL /*fArriving*/, + __in_opt LPVOID /*pvContext*/ + ) + { + } + + static void MonDirectory( + __in HRESULT hrResult, + __in_z LPCWSTR wzPath, + __in_z BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvDirectoryContext + ) + { + Assert::Equal(S_OK, hrResult); + Assert::Equal(0, reinterpret_cast(pvDirectoryContext)); + + HRESULT hr = S_OK; + Results *pResults = reinterpret_cast(pvContext); + + hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5); + NativeAssert::ValidReturnCode(hr, S_OK); + ++pResults->cDirectories; + + pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult; + pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath; + pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive; + } + + static void MonRegKey( + __in HRESULT hrResult, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in_z BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvRegKeyContext + ) + { + Assert::Equal(S_OK, hrResult); + Assert::Equal(0, reinterpret_cast(pvRegKeyContext)); + + HRESULT hr = S_OK; + Results *pResults = reinterpret_cast(pvContext); + + hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5); + NativeAssert::ValidReturnCode(hr, S_OK); + ++pResults->cRegKeys; + + pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult; + pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot; + pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey; + pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness; + pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive; + } + + public ref class MonUtil + { + public: + void ClearResults(Results *pResults) + { + ReleaseNullMem(pResults->rgDirectories); + pResults->cDirectories = 0; + ReleaseNullMem(pResults->rgRegKeys); + pResults->cRegKeys = 0; + } + + void RemoveDirectory(LPCWSTR wzPath) + { + DWORD dwRetryCount = 0; + const DWORD c_dwMaxRetryCount = 100; + const DWORD c_dwRetryInterval = 50; + + HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE); + + // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed + // (and deletion will be "pending" until our monitor handle is closed) + // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete() + // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later + // (after the waiter thread wakes up, it will release the handle) + while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount) + { + ::Sleep(c_dwRetryInterval); + ++dwRetryCount; + hr = DirEnsureDelete(wzPath, TRUE, TRUE); + } + + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + } + + void TestDirectory(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPWSTR sczShallowPath = NULL; + LPWSTR sczParentPath = NULL; + LPWSTR sczDeepPath = NULL; + LPWSTR sczChildPath = NULL; + LPWSTR sczChildFilePath = NULL; + + try + { + hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + RemoveDirectory(sczShallowPath); + + hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = DirEnsureExists(sczParentPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + // Make sure creating the parent directory does nothing, even after silence period + ::Sleep(FULLWAIT); + Assert::Equal(0, pResults->cDirectories); + + // Now create the target path, no notification until after the silence period + hr = DirEnsureExists(sczDeepPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(0, pResults->cDirectories); + + // Now after the full silence period, it should have triggered + ::Sleep(POSTWAIT); + Assert::Equal(1, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK); + + // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. + RemoveDirectory(sczShallowPath); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK); + + // Create the parent directory again, still should be nothing even after full silence period + hr = DirEnsureExists(sczParentPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + + hr = DirEnsureExists(sczChildPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(2, pResults->cDirectories); + + ::Sleep(POSTWAIT); + Assert::Equal(3, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); + + // Write a file to a deep child subfolder, and make sure it's detected + hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + ::Sleep(PREWAIT); + Assert::Equal(3, pResults->cDirectories); + + ::Sleep(POSTWAIT); + Assert::Equal(4, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); + + RemoveDirectory(sczParentPath); + + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); + + // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked + hr = MonRemoveDirectory(handle, sczDeepPath, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + ::Sleep(PREWAIT); + + hr = DirEnsureExists(sczDeepPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); + + // Finally, add it back so we can test multiple things to monitor at once + hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + finally + { + ReleaseStr(sczShallowPath); + ReleaseStr(sczDeepPath); + ReleaseStr(sczParentPath); + } + } + + void TestRegKey(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\"; + LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\"; + LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\"; + LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\"; + HKEY hk = NULL; + + try + { + hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + + hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + ReleaseRegKey(hk); + // Make sure creating the parent key does nothing, even after silence period + ::Sleep(FULLWAIT); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + Assert::Equal(0, pResults->cRegKeys); + + // Now create the target path, no notification until after the silence period + hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(PREWAIT); + Assert::Equal(0, pResults->cRegKeys); + + // Now after the full silence period, it should have triggered + ::Sleep(POSTWAIT); + Assert::Equal(1, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK); + + // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. + hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + ::Sleep(PREWAIT); + Assert::Equal(1, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK); + + // Create the parent directory again, still should be nothing even after full silence period + hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cRegKeys); + + hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(2, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(3, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); + + // Write a registry value to some deep child subkey, and make sure it's detected + hr = RegWriteString(hk, L"valuename", L"testvalue"); + NativeAssert::ValidReturnCode(hr, S_OK); + ReleaseRegKey(hk); + ::Sleep(PREWAIT); + Assert::Equal(3, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(4, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); + + hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cRegKeys); + + // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked + hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cRegKeys); + } + finally + { + ReleaseRegKey(hk); + } + } + + void TestMoreThan64(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPWSTR sczBaseDir = NULL; + LPWSTR sczDir = NULL; + LPWSTR sczFile = NULL; + + try + { + hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + for (DWORD i = 0; i < 200; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = DirEnsureExists(sczDir, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + + hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + + hr = PathConcat(sczDir, L"file.txt", &sczFile); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(1, pResults->cDirectories); + + for (DWORD i = 0; i < 199; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = MonRemoveDirectory(handle, sczDir, FALSE); + NativeAssert::ValidReturnCode(hr, S_OK); + } + ::Sleep(FULLWAIT); + + hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + + for (DWORD i = 0; i < 199; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + ::Sleep(FULLWAIT); + + hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(3, pResults->cDirectories); + } + finally + { + ReleaseStr(sczBaseDir); + ReleaseStr(sczDir); + ReleaseStr(sczFile); + } + } + + [Fact(Skip = "Test demonstrates failure")] + void MonUtilTest() + { + HRESULT hr = S_OK; + MON_HANDLE handle = NULL; + List^ gcHandles = gcnew List(); + Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE); + Assert::True(NULL != pResults); + + try + { + // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild + MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral); + GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral); + gcHandles->Add(gchMonGeneral); + IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral); + + MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus); + GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus); + gcHandles->Add(gchMonDriveStatus); + IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus); + + MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory); + GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory); + gcHandles->Add(gchMonDirectory); + IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory); + + MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey); + GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey); + gcHandles->Add(gchMonRegKey); + IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey); + + // "Silence period" is 100 ms + hr = MonCreate(&handle, static_cast(ipMonGeneral.ToPointer()), static_cast(ipMonDriveStatus.ToPointer()), static_cast(ipMonDirectory.ToPointer()), static_cast(ipMonRegKey.ToPointer()), pResults); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegInitialize(); + NativeAssert::ValidReturnCode(hr, S_OK); + + TestDirectory(handle, pResults); + ClearResults(pResults); + TestRegKey(handle, pResults); + ClearResults(pResults); + TestMoreThan64(handle, pResults); + ClearResults(pResults); + } + finally + { + ReleaseMon(handle); + + for each (GCHandle gcHandle in gcHandles) + { + gcHandle.Free(); + } + + ReleaseMem(pResults->rgDirectories); + ReleaseMem(pResults->rgRegKeys); + ReleaseMem(pResults); + } + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class PathUtil + { + public: + [Fact] + void PathGetHierarchyArrayTest() + { + HRESULT hr = S_OK; + LPWSTR *rgsczPaths = NULL; + UINT cPaths = 0; + + try + { + hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\a.txt", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path"); + Assert::Equal(5, cPaths); + NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\a.txt", rgsczPaths[4]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path"); + Assert::Equal(4, cPaths); + NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\file.txt", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path"); + Assert::Equal(3, cPaths); + NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\file.txt", rgsczPaths[2]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(2, cPaths); + NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\ValueName", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(4, cPaths); + NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\ValueName", rgsczPaths[3]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(3, cPaths); + NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); + ReleaseNullStrArray(rgsczPaths, cPaths); + } + finally + { + ReleaseStrArray(rgsczPaths, cPaths); + } + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include +#include + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);}; + +namespace DutilTests +{ + enum TABLES + { + TABLE_A, + TABLE_COUNT + }; + + enum TABLE_A_COLUMNS + { + TABLE_A_KEY, + TABLE_A_BINARY, + TABLE_A_DWORD, + TABLE_A_QWORD, + TABLE_A_BOOL, + TABLE_A_STRING, + TABLE_A_DWORD_NULLABLE, + TABLE_A_INITIAL_COLUMNS, + + TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS, + TABLE_A_FINAL_COLUMNS + }; + + struct TableARowValue + { + DWORD dwAutoGenKey; + + BYTE *pbBinary; + DWORD cBinary; + + DWORD dw; + DWORD64 qw; + BOOL f; + LPWSTR scz; + + BOOL fNullablePresent; + DWORD dwNullable; + + BOOL fSchemaV2; + LPWSTR sczExtra; + }; + + public ref class SceUtil + { + public: + void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema) + { + DWORD dwTable; + + for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns); + ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes); + } + + ReleaseMem(pdsSchema->rgTables); + + return; + } + + void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended) + { + pSchema->cTables = TABLE_COUNT; + pSchema->rgTables = static_cast(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE)); + NativeAssert::True(pSchema->rgTables != NULL); + + pSchema->rgTables[TABLE_A].wzName = L"TableA"; + pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS; + pSchema->rgTables[TABLE_A].cIndexes = 2; + + for (DWORD i = 0; i < pSchema->cTables; ++i) + { + pSchema->rgTables[i].rgColumns = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE)); + NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL); + + pSchema->rgTables[i].rgIndexes = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE)); + NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL); + } + + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE; + + if (fIncludeExtended) + { + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE; + } + + static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD }; + static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING }; + + ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword"); + ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String"); + } + + void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra) + { + pValue->pbBinary = pbBinary; + pValue->cBinary = cBinary; + pValue->dw = dw; + pValue->qw = qw; + pValue->f = f; + pValue->scz = scz; + + if (pdw) + { + pValue->fNullablePresent = TRUE; + pValue->dwNullable = *pdw; + } + else + { + pValue->fNullablePresent = FALSE; + } + + if (sczExtra) + { + pValue->fSchemaV2 = TRUE; + pValue->sczExtra = sczExtra; + } + else + { + pValue->fSchemaV2 = FALSE; + } + } + + void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther) + { + NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary); + NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary)); + + NativeAssert::Equal(pValueExpected->dw, pValueOther->dw); + NativeAssert::Equal(pValueExpected->qw, pValueOther->qw); + NativeAssert::Equal(pValueExpected->f, pValueOther->f); + NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz)); + + NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent); + if (pValueExpected->fNullablePresent) + { + NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable); + } + + NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2); + if (pValueExpected->fSchemaV2) + { + NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra)); + } + } + + void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback) + { + HRESULT hr = S_OK; + SCE_ROW_HANDLE sceRow = NULL; + + hr = SceBeginTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to begin transaction"); + + hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow); + NativeAssert::Succeeded(hr, "Failed to prepare to insert row"); + + hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary); + NativeAssert::Succeeded(hr, "Failed to set binary value"); + + hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw); + NativeAssert::Succeeded(hr, "Failed to set dword value"); + + hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw); + NativeAssert::Succeeded(hr, "Failed to set qword value"); + + hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f); + NativeAssert::Succeeded(hr, "Failed to set bool value"); + + hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz); + NativeAssert::Succeeded(hr, "Failed to set string value"); + + if (pValue->fNullablePresent) + { + hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable); + NativeAssert::Succeeded(hr, "Failed to set dword value"); + } + else + { + hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE); + NativeAssert::Succeeded(hr, "Failed to set null value"); + } + + if (pValue->fSchemaV2) + { + hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra); + NativeAssert::Succeeded(hr, "Failed to set extra string value"); + } + + hr = SceFinishUpdate(sceRow); + NativeAssert::Succeeded(hr, "Failed to finish insert"); + + if (fRollback) + { + hr = SceRollbackTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to rollback transaction"); + } + else + { + hr = SceCommitTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to commit transaction"); + + hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey); + NativeAssert::Succeeded(hr, "Failed to get autogen key after insert"); + + NativeAssert::True(pValue->dwAutoGenKey != 0); + } + + ReleaseSceRow(sceRow); + } + + void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow) + { + HRESULT hr = S_OK; + TableARowValue value = {}; + + hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary); + NativeAssert::Succeeded(hr, "Failed to get binary value from result row"); + + hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw); + NativeAssert::Succeeded(hr, "Failed to get dword value from result row"); + + hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw); + NativeAssert::Succeeded(hr, "Failed to get qword value from result row"); + + hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f); + NativeAssert::Succeeded(hr, "Failed to get bool value from result row"); + + hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz); + NativeAssert::Succeeded(hr, "Failed to get string value from result row"); + + hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable); + if (hr == E_NOTFOUND) + { + value.fNullablePresent = FALSE; + hr = S_OK; + } + else + { + NativeAssert::Succeeded(hr, "Failed to get string value from result row"); + value.fNullablePresent = TRUE; + } + + if (pExpectedValue->fSchemaV2) + { + value.fSchemaV2 = TRUE; + hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra); + NativeAssert::Succeeded(hr, "Failed to get extra string value from result row"); + } + + AssertStructValuesSame(pExpectedValue, &value); + + ReleaseNullMem(value.pbBinary); + ReleaseNullStr(value.scz); + } + + void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults) + { + HRESULT hr = S_OK; + SCE_ROW_HANDLE sceRow = NULL; + + for (DWORD i = 0; i < cExpectedValues; ++i) + { + hr = SceGetNextResultRow(queryResults, &sceRow); + NativeAssert::Succeeded(hr, "Failed to get next result row"); + + VerifyRow(rgExpectedValues[i], sceRow); + ReleaseNullSceRow(sceRow); + } + + // No more results + NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow))); + } + + void TestIndex(SCE_DATABASE *pDatabase) + { + HRESULT hr = S_OK; + BYTE binary1[50] = { 0x80, 0x70 }; + BYTE binary2[40] = { 0x90, 0xAB }; + BYTE binary3[40] = { 0x85, 0x88 }; + DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888; + TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {}; + SCE_QUERY_HANDLE query = NULL; + SCE_QUERY_RESULTS_HANDLE results = NULL; + + SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL); + SetStructValues(&value2, static_cast(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL); + SetStructValues(&value3, static_cast(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL); + SetStructValues(&value4, static_cast(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL); + SetStructValues(&value5, static_cast(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL); + + // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards + InsertRow(pDatabase, &value1, TRUE); + + InsertRow(pDatabase, &value1, FALSE); + InsertRow(pDatabase, &value2, FALSE); + InsertRow(pDatabase, &value3, FALSE); + InsertRow(pDatabase, &value4, FALSE); + InsertRow(pDatabase, &value5, FALSE); + + NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey); + + // Test setting 1 column + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 }; + VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns, third column is unspecified so results are sorted by it + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery2[] = { &value5, &value2 }; + VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns, third column of index is unspecified so results are sorted by it + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery3[] = { &value5, &value2 }; + VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by + hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery4[] = { &value2, &value5 }; + VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results); + ReleaseNullSceQueryResults(results); + } + + void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase) + { + HRESULT hr = S_OK; + BYTE binary1[40] = { 0x55, 0x44 }; + DWORD dwValue1 = 58; + TableARowValue value1 = {}; + SCE_QUERY_HANDLE query = NULL; + SCE_ROW_HANDLE row = NULL; + + SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring"); + + InsertRow(pDatabase, &value1, FALSE); + + // Test setting 1 column + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 5); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryExact(&query, &row); + NativeAssert::Succeeded(hr, "Failed to run query exact"); + + VerifyRow(&value1, row); + } + + [Fact] + void SceUtilTest() + { + HRESULT hr = S_OK; + BOOL fComInitialized = FALSE; + LPWSTR sczDbPath = NULL; + SCE_DATABASE *pDatabase = NULL; + SCE_DATABASE_SCHEMA schema1 = {}; + SCE_DATABASE_SCHEMA schema2 = {}; + + try + { + hr = ::CoInitialize(0); + NativeAssert::Succeeded(hr, "Failed to initialize COM"); + fComInitialized = TRUE; + + SetupSchema(&schema1, FALSE); + SetupSchema(&schema2, TRUE); + + hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to test database"); + + FileEnsureDelete(sczDbPath); + + hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase); + NativeAssert::Succeeded(hr, "Failed to ensure database schema"); + + TestIndex(pDatabase); + + hr = SceCloseDatabase(pDatabase); + pDatabase = NULL; + NativeAssert::Succeeded(hr, "Failed to close database"); + + // Add column to schema + hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase); + NativeAssert::Succeeded(hr, "Failed to ensure database schema"); + + TestReadWriteSchemaV2(pDatabase); + } + finally + { + ReleaseSceSchema(&schema1); + ReleaseSceSchema(&schema2); + + if (NULL != pDatabase) + { + hr = SceCloseDatabase(pDatabase); + NativeAssert::Succeeded(hr, "Failed to close database"); + } + ReleaseStr(sczDbPath); + + if (fComInitialized) + { + ::CoUninitialize(); + } + } + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class StrUtil + { + public: + [Fact] + void StrUtilFormattedTest() + { + HRESULT hr = S_OK; + LPWSTR sczText = NULL; + + try + { + hr = StrAllocFormatted(&sczText, L"%hs - %ls - %u", "ansi string", L"unicode string", 1234); + NativeAssert::Succeeded(hr, "Failed to format string."); + NativeAssert::StringEqual(L"ansi string - unicode string - 1234", sczText); + + ReleaseNullStr(sczText); + + hr = StrAllocString(&sczText, L"repeat", 0); + NativeAssert::Succeeded(hr, "Failed to allocate string."); + + hr = StrAllocFormatted(&sczText, L"%ls and %ls", sczText, sczText); + NativeAssert::Succeeded(hr, "Failed to format string unto itself."); + NativeAssert::StringEqual(L"repeat and repeat", sczText); + } + finally + { + ReleaseStr(sczText); + } + } + + [Fact] + void StrUtilTrimTest() + { + TestTrim(L"", L""); + TestTrim(L"Blah", L"Blah"); + TestTrim(L"\t\t\tBlah", L"Blah"); + TestTrim(L"\t Blah ", L"Blah"); + TestTrim(L"Blah ", L"Blah"); + TestTrim(L"\t Spaces \t Between \t", L"Spaces \t Between"); + TestTrim(L" \t\t\t ", L""); + + TestTrimAnsi("", ""); + TestTrimAnsi("Blah", "Blah"); + TestTrimAnsi("\t\t\tBlah", "Blah"); + TestTrimAnsi(" Blah ", "Blah"); + TestTrimAnsi("Blah ", "Blah"); + TestTrimAnsi("\t Spaces \t Between \t", "Spaces \t Between"); + TestTrimAnsi(" \t\t\t ", ""); + } + + [Fact] + void StrUtilConvertTest() + { + char a[] = { 'a', 'b', 'C', 'd', '\0', '\0' }; + + TestStrAllocStringAnsi(a, 5, L"abCd"); + TestStrAllocStringAnsi(a, 4, L"abCd"); + TestStrAllocStringAnsi(a, 3, L"abC"); + TestStrAllocStringAnsi(a, 2, L"ab"); + TestStrAllocStringAnsi(a, 1, L"a"); + TestStrAllocStringAnsi(a, 0, L"abCd"); + + wchar_t b[] = { L'a', L'b', L'C', L'd', L'\0', L'\0' }; + + TestStrAnsiAllocString(b, 5, "abCd"); + TestStrAnsiAllocString(b, 4, "abCd"); + TestStrAnsiAllocString(b, 3, "abC"); + TestStrAnsiAllocString(b, 2, "ab"); + TestStrAnsiAllocString(b, 1, "a"); + TestStrAnsiAllocString(b, 0, "abCd"); + } + + private: + void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult) + { + HRESULT hr = S_OK; + LPWSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrTrimWhitespace(&sczOutput, wzInput); + NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: {0}", wzInput); + + if (0 != wcscmp(wzExpectedResult, sczOutput)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Trimmed string \"%ls\", expected result \"%ls\", actual result \"%ls\"", wzInput, wzExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + + void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult) + { + HRESULT hr = S_OK; + LPSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrAnsiTrimWhitespace(&sczOutput, szInput); + NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: \"{0}\"", szInput); + + if (0 != strcmp(szExpectedResult, sczOutput)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Trimmed string \"%hs\", expected result \"%hs\", actual result \"%hs\"", szInput, szExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + + void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult) + { + HRESULT hr = S_OK; + LPWSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8); + NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", szSource); + + if (0 != wcscmp(sczOutput, wzExpectedResult)) + { + hr = E_FAIL; + ExitOnFailure(hr, "String doesn't match, expected result \"%ls\", actual result \"%ls\"", wzExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + + void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult) + { + HRESULT hr = S_OK; + LPSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8); + NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", wzSource); + + if (0 != strcmp(sczOutput, szExpectedResult)) + { + hr = E_FAIL; + ExitOnFailure(hr, "String doesn't match, expected result \"%hs\", actual result \"%hs\"", szExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + }; +} 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 @@ + + + + + + BundleB v2.0 + Bundle Subtitle. + 1116353B-7C6E-4C29-BFA1-D4A972CD421D + 2014-07-14T12:39:00.000Z + http://localhost:9999/wix4/BundleB/feed + + manual build + + Bundle v2.0 + v2.0 + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Updated release.</li> + </ul> + + 2.0.0.0 + 2014-11-10T12:39:00.000Z + + + Bundle v1.0 + v1.0 + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Initial release.</li> + </ul> + + + 1.0.0.0 + 2014-11-09T12:39:00.000Z + + + Bundle v1.0-preview + v1.0-preview + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Initial release.</li> + </ul> + + 1.0.0.0 + 2014-11-09T12:39:00.000Z + + 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define VER_APP +#define VER_ORIGINAL_FILENAME "UnitTest.dll" +#define VER_INTERNAL_NAME "setup" +#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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace System::Text; +using namespace System::Collections::Generic; +using namespace Xunit; + +namespace CfgTests +{ + public ref class UriUtil + { + public: + [Fact] + void UriProtocolTest() + { + HRESULT hr = S_OK; + + DutilInitialize(&DutilTestTraceError); + + LPCWSTR uri = L"https://localhost/"; + URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HTTPS://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HtTpS://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HTTP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"http://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"HtTp://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"file://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FILE://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FiLe://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FTP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + uri = L"ftp://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + uri = L"FtP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + LExit: + DutilUninitialize(); + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class VerUtil + { + public: + [Fact] + void VerCompareVersionsTreatsMissingRevisionAsZero() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"1.2.3.4"; + LPCWSTR wzVersion2 = L"1.2.3"; + LPCWSTR wzVersion3 = L"1.2.3.0"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(4, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(5, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(2, pVersion3->dwMinor); + Assert::Equal(3, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(7, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"1.0-2.0"; + LPCWSTR wzVersion2 = L"1.0-19"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(2, pVersion1->cReleaseLabels); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion1->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); + Assert::Equal(0, pVersion1->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); + Assert::Equal(6, pVersion1->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(1, pVersion2->cReleaseLabels); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(19, pVersion2->rgReleaseLabels[0].dwValue); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCompareVersionsHandlesNormallyInvalidVersions() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + VERUTIL_VERSION* pVersion5 = NULL; + VERUTIL_VERSION* pVersion6 = NULL; + LPCWSTR wzVersion1 = L"10.-4.0"; + LPCWSTR wzVersion2 = L"10.-2.0"; + LPCWSTR wzVersion3 = L"0"; + LPCWSTR wzVersion4 = L""; + LPCWSTR wzVersion5 = L"10-2"; + LPCWSTR wzVersion6 = L"10-4.@"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); + + hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(10, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(3, pVersion1->cchMetadataOffset); + Assert::Equal(TRUE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(10, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(3, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(0, pVersion3->dwMajor); + Assert::Equal(0, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(1, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(0, pVersion4->dwMajor); + Assert::Equal(0, pVersion4->dwMinor); + Assert::Equal(0, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(0, pVersion4->cReleaseLabels); + Assert::Equal(0, pVersion4->cchMetadataOffset); + Assert::Equal(TRUE, pVersion4->fInvalid); + + NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); + Assert::Equal(10, pVersion5->dwMajor); + Assert::Equal(0, pVersion5->dwMinor); + Assert::Equal(0, pVersion5->dwPatch); + Assert::Equal(0, pVersion5->dwRevision); + Assert::Equal(1, pVersion5->cReleaseLabels); + + Assert::Equal(TRUE, pVersion5->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion5->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion5->rgReleaseLabels[0].cchLabel); + Assert::Equal(3, pVersion5->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(4, pVersion5->cchMetadataOffset); + Assert::Equal(FALSE, pVersion5->fInvalid); + + NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); + Assert::Equal(10, pVersion6->dwMajor); + Assert::Equal(0, pVersion6->dwMinor); + Assert::Equal(0, pVersion6->dwPatch); + Assert::Equal(0, pVersion6->dwRevision); + Assert::Equal(1, pVersion6->cReleaseLabels); + + Assert::Equal(TRUE, pVersion6->rgReleaseLabels[0].fNumeric); + Assert::Equal(4, pVersion6->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion6->rgReleaseLabels[0].cchLabel); + Assert::Equal(3, pVersion6->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(5, pVersion6->cchMetadataOffset); + Assert::Equal(TRUE, pVersion6->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1); + TestVerutilCompareParsedVersions(pVersion5, pVersion6, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + ReleaseVerutilVersion(pVersion5); + ReleaseVerutilVersion(pVersion6); + } + } + + [Fact] + void VerCompareVersionsTreatsHyphenAsVersionSeparator() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"0.0.1-a"; + LPCWSTR wzVersion2 = L"0-2"; + LPCWSTR wzVersion3 = L"1-2"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(1, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(1, pVersion1->cReleaseLabels); + + Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(1, pVersion2->cReleaseLabels); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(3, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(0, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(1, pVersion3->cReleaseLabels); + + Assert::Equal(TRUE, pVersion3->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion3->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion3->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(3, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsIgnoresLeadingZeroes() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + LPCWSTR wzVersion1 = L"0.01-a.1"; + LPCWSTR wzVersion2 = L"0.1.0-a.1"; + LPCWSTR wzVersion3 = L"0.1-a.b.0"; + LPCWSTR wzVersion4 = L"0.1.0-a.b.000"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(1, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(2, pVersion1->cReleaseLabels); + + Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(5, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); + Assert::Equal(7, pVersion1->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(8, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(1, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(2, pVersion2->cReleaseLabels); + + Assert::Equal(FALSE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion2->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion2->rgReleaseLabels[1].cchLabel); + Assert::Equal(8, pVersion2->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(9, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(0, pVersion3->dwMajor); + Assert::Equal(1, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(3, pVersion3->cReleaseLabels); + + Assert::Equal(FALSE, pVersion3->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion3->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pVersion3->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion3->rgReleaseLabels[1].cchLabel); + Assert::Equal(6, pVersion3->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(TRUE, pVersion3->rgReleaseLabels[2].fNumeric); + Assert::Equal(0, pVersion3->rgReleaseLabels[2].dwValue); + Assert::Equal(1, pVersion3->rgReleaseLabels[2].cchLabel); + Assert::Equal(8, pVersion3->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(9, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(0, pVersion4->dwMajor); + Assert::Equal(1, pVersion4->dwMinor); + Assert::Equal(0, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(3, pVersion4->cReleaseLabels); + + Assert::Equal(FALSE, pVersion4->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion4->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion4->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pVersion4->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion4->rgReleaseLabels[1].cchLabel); + Assert::Equal(8, pVersion4->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(TRUE, pVersion4->rgReleaseLabels[2].fNumeric); + Assert::Equal(0, pVersion4->rgReleaseLabels[2].dwValue); + Assert::Equal(3, pVersion4->rgReleaseLabels[2].cchLabel); + Assert::Equal(10, pVersion4->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(13, pVersion4->cchMetadataOffset); + Assert::Equal(FALSE, pVersion4->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + } + } + + [Fact] + void VerCompareVersionsTreatsUnexpectedContentAsMetadata() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"1.2.3+abcd"; + LPCWSTR wzVersion2 = L"1.2.3.abcd"; + LPCWSTR wzVersion3 = L"1.2.3.-abcd"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(6, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(2, pVersion3->dwMinor); + Assert::Equal(3, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(6, pVersion3->cchMetadataOffset); + Assert::Equal(TRUE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1); + TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsIgnoresLeadingV() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"10.20.30.40"; + LPCWSTR wzVersion2 = L"v10.20.30.40"; + LPCWSTR wzVersion3 = L"V10.20.30.40"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(10, pVersion1->dwMajor); + Assert::Equal(20, pVersion1->dwMinor); + Assert::Equal(30, pVersion1->dwPatch); + Assert::Equal(40, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(11, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion); + Assert::Equal(10, pVersion2->dwMajor); + Assert::Equal(20, pVersion2->dwMinor); + Assert::Equal(30, pVersion2->dwPatch); + Assert::Equal(40, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(11, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion); + Assert::Equal(10, pVersion3->dwMajor); + Assert::Equal(20, pVersion3->dwMinor); + Assert::Equal(30, pVersion3->dwPatch); + Assert::Equal(40, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(11, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsHandlesTooLargeNumbers() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295"; + LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(4294967295, pVersion1->dwMajor); + Assert::Equal(4294967295, pVersion1->dwMinor); + Assert::Equal(4294967295, pVersion1->dwPatch); + Assert::Equal(4294967295, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(43, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(0, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCompareVersionsIgnoresMetadataForValidVersions() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"1.2.3+abc"; + LPCWSTR wzVersion2 = L"1.2.3+xyz"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(6, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCopyVersionCopiesVersion() + { + HRESULT hr = S_OK; + LPCWSTR wzVersion = L"1.2.3.4+abc123"; + VERUTIL_VERSION* pSource = NULL; + VERUTIL_VERSION* pCopy = NULL; + int nResult = 0; + + try + { + hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); + NativeAssert::Succeeded(hr, "VerParseVersion failed"); + + NativeAssert::StringEqual(wzVersion, pSource->sczVersion); + Assert::Equal(1, pSource->dwMajor); + Assert::Equal(2, pSource->dwMinor); + Assert::Equal(3, pSource->dwPatch); + Assert::Equal(4, pSource->dwRevision); + Assert::Equal(0, pSource->cReleaseLabels); + + Assert::Equal(8, pSource->cchMetadataOffset); + Assert::Equal(FALSE, pSource->fInvalid); + + hr = VerCopyVersion(pSource, &pCopy); + NativeAssert::Succeeded(hr, "VerCopyVersion failed"); + + Assert::False(pSource == pCopy); + Assert::False(pSource->sczVersion == pCopy->sczVersion); + + hr = VerCompareParsedVersions(pSource, pCopy, &nResult); + NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); + + Assert::Equal(nResult, 0); + } + finally + { + ReleaseVerutilVersion(pCopy); + ReleaseVerutilVersion(pSource); + } + } + + [Fact] + void VerCopyVersionCopiesPrereleaseVersion() + { + HRESULT hr = S_OK; + LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123"; + VERUTIL_VERSION* pSource = NULL; + VERUTIL_VERSION* pCopy = NULL; + int nResult = 0; + + try + { + hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); + NativeAssert::Succeeded(hr, "VerParseVersion failed"); + + NativeAssert::StringEqual(wzVersion, pSource->sczVersion); + Assert::Equal(1, pSource->dwMajor); + Assert::Equal(2, pSource->dwMinor); + Assert::Equal(3, pSource->dwPatch); + Assert::Equal(4, pSource->dwRevision); + Assert::Equal(5, pSource->cReleaseLabels); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[0].cchLabel); + Assert::Equal(8, pSource->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[1].cchLabel); + Assert::Equal(10, pSource->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[2].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[2].cchLabel); + Assert::Equal(12, pSource->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[3].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[3].cchLabel); + Assert::Equal(14, pSource->rgReleaseLabels[3].cchLabelOffset); + + Assert::Equal(TRUE, pSource->rgReleaseLabels[4].fNumeric); + Assert::Equal(5, pSource->rgReleaseLabels[4].dwValue); + Assert::Equal(1, pSource->rgReleaseLabels[4].cchLabel); + Assert::Equal(16, pSource->rgReleaseLabels[4].cchLabelOffset); + + Assert::Equal(18, pSource->cchMetadataOffset); + Assert::Equal(TRUE, pSource->fInvalid); + + hr = VerCopyVersion(pSource, &pCopy); + NativeAssert::Succeeded(hr, "VerCopyVersion failed"); + + Assert::False(pSource == pCopy); + Assert::False(pSource->sczVersion == pCopy->sczVersion); + Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels); + + hr = VerCompareParsedVersions(pSource, pCopy, &nResult); + NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); + + Assert::Equal(nResult, 0); + } + finally + { + ReleaseVerutilVersion(pCopy); + ReleaseVerutilVersion(pSource); + } + } + + [Fact] + void VerParseVersionTreatsTrailingDotsAsInvalid() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + VERUTIL_VERSION* pVersion5 = NULL; + VERUTIL_VERSION* pVersion6 = NULL; + VERUTIL_VERSION* pVersion7 = NULL; + LPCWSTR wzVersion1 = L"."; + LPCWSTR wzVersion2 = L"1."; + LPCWSTR wzVersion3 = L"2.1."; + LPCWSTR wzVersion4 = L"3.2.1."; + LPCWSTR wzVersion5 = L"4.3.2.1."; + LPCWSTR wzVersion6 = L"5-."; + LPCWSTR wzVersion7 = L"6-a."; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); + + hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); + + hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(0, pVersion1->cchMetadataOffset); + Assert::Equal(TRUE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(2, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(2, pVersion3->dwMajor); + Assert::Equal(1, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(4, pVersion3->cchMetadataOffset); + Assert::Equal(TRUE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(3, pVersion4->dwMajor); + Assert::Equal(2, pVersion4->dwMinor); + Assert::Equal(1, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(0, pVersion4->cReleaseLabels); + Assert::Equal(6, pVersion4->cchMetadataOffset); + Assert::Equal(TRUE, pVersion4->fInvalid); + + NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); + Assert::Equal(4, pVersion5->dwMajor); + Assert::Equal(3, pVersion5->dwMinor); + Assert::Equal(2, pVersion5->dwPatch); + Assert::Equal(1, pVersion5->dwRevision); + Assert::Equal(0, pVersion5->cReleaseLabels); + Assert::Equal(8, pVersion5->cchMetadataOffset); + Assert::Equal(TRUE, pVersion5->fInvalid); + + NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); + Assert::Equal(5, pVersion6->dwMajor); + Assert::Equal(0, pVersion6->dwMinor); + Assert::Equal(0, pVersion6->dwPatch); + Assert::Equal(0, pVersion6->dwRevision); + Assert::Equal(0, pVersion6->cReleaseLabels); + Assert::Equal(2, pVersion6->cchMetadataOffset); + Assert::Equal(TRUE, pVersion6->fInvalid); + + NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion); + Assert::Equal(6, pVersion7->dwMajor); + Assert::Equal(0, pVersion7->dwMinor); + Assert::Equal(0, pVersion7->dwPatch); + Assert::Equal(0, pVersion7->dwRevision); + Assert::Equal(1, pVersion7->cReleaseLabels); + + Assert::Equal(FALSE, pVersion7->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion7->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion7->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(4, pVersion7->cchMetadataOffset); + Assert::Equal(TRUE, pVersion7->fInvalid); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + ReleaseVerutilVersion(pVersion5); + ReleaseVerutilVersion(pVersion6); + ReleaseVerutilVersion(pVersion7); + } + } + + [Fact] + void VerVersionFromQwordCreatesVersion() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + + try + { + hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1); + NativeAssert::Succeeded(hr, "VerVersionFromQword failed"); + + NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(4, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + } + finally + { + ReleaseVerutilVersion(pVersion1); + } + } + + private: + void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult) + { + HRESULT hr = S_OK; + int nResult = 0; + + hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); + NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); + + Assert::Equal(nExpectedResult, nResult); + + hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult); + NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); + + Assert::Equal(nExpectedResult, -nResult); + } + }; +} 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const int ERROR_STRING_BUFFER = 1024; + +static char szMsg[ERROR_STRING_BUFFER]; +static WCHAR wzMsg[ERROR_STRING_BUFFER]; + +void CALLBACK DutilTestTraceError( + __in_z LPCSTR /*szFile*/, + __in int /*iLine*/, + __in REPORT_LEVEL /*rl*/, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + if (DUTIL_SOURCE_EXTERNAL == source) + { + ::StringCchPrintfA(szMsg, countof(szMsg), szFormat, args); + MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); + throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrError, gcnew System::String(wzMsg))); + } +} 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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL + +void CALLBACK DutilTestTraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); 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 @@ + + + + + + + + + + + + + \ 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#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 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + +// Include error.h before dutil.h +#include +#include "error.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // NOTE: this must come after atomutil.h and rssutil.h since it uses them. +#include +#include + +#pragma managed +#include 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 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + diff --git a/src/signing.json b/src/signing.json new file mode 100644 index 00000000..fe1c8c9b --- /dev/null +++ b/src/signing.json @@ -0,0 +1,13 @@ +{ + "SignClient": { + "AzureAd": { + "AADInstance": "https://login.microsoftonline.com/", + "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", + "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" + }, + "Service": { + "Url": "https://codesign.dotnetfoundation.org/", + "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" + } + } +} diff --git a/src/test/DUtilUnitTest/ApupUtilTests.cpp b/src/test/DUtilUnitTest/ApupUtilTests.cpp deleted file mode 100644 index 30a45f5a..00000000 --- a/src/test/DUtilUnitTest/ApupUtilTests.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class ApupUtil - { - public: - [Fact] - void AllocChainFromAtomSortsDescending() - { - HRESULT hr = S_OK; - ATOM_FEED* pFeed = NULL; - APPLICATION_UPDATE_CHAIN* pChain = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - XmlInitialize(); - NativeAssert::Succeeded(hr, "Failed to initialize Xml."); - - pin_ptr feedFilePath = PtrToStringChars(TestData::Get("TestData", "ApupUtilTests", "FeedBv2.0.xml")); - hr = AtomParseFromFile(feedFilePath, &pFeed); - NativeAssert::Succeeded(hr, "Failed to parse feed: {0}", feedFilePath); - - hr = ApupAllocChainFromAtom(pFeed, &pChain); - NativeAssert::Succeeded(hr, "Failed to get chain from feed."); - - Assert::Equal(3ul, pChain->cEntries); - NativeAssert::StringEqual(L"Bundle v2.0", pChain->rgEntries[0].wzTitle); - NativeAssert::StringEqual(L"Bundle v1.0", pChain->rgEntries[1].wzTitle); - NativeAssert::StringEqual(L"Bundle v1.0-preview", pChain->rgEntries[2].wzTitle); - } - finally - { - DutilUninitialize(); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/AssemblyInfo.cpp b/src/test/DUtilUnitTest/AssemblyInfo.cpp deleted file mode 100644 index 2d527910..00000000 --- a/src/test/DUtilUnitTest/AssemblyInfo.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; - -[assembly: AssemblyTitleAttribute("Windows Installer XML Dutil unit tests")]; -[assembly: AssemblyDescriptionAttribute("Dutil unit tests")]; -[assembly: AssemblyCultureAttribute("")]; -[assembly: ComVisible(false)]; diff --git a/src/test/DUtilUnitTest/DUtilTests.cpp b/src/test/DUtilUnitTest/DUtilTests.cpp deleted file mode 100644 index 55e81d46..00000000 --- a/src/test/DUtilUnitTest/DUtilTests.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class DUtil - { - public: - [Fact] - void DUtilTraceErrorSourceFiltersOnTraceLevel() - { - DutilInitialize(&DutilTestTraceError); - - CallDutilTraceErrorSource(); - - Dutil_TraceSetLevel(REPORT_DEBUG, FALSE); - - Action^ action = gcnew Action(this, &DUtil::CallDutilTraceErrorSource); - Assert::Throws(action); - - DutilUninitialize(); - } - - private: - void CallDutilTraceErrorSource() - { - Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, DUTIL_SOURCE_EXTERNAL, E_FAIL, "Error message"); - } - }; -} diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj deleted file mode 100644 index 18410e5d..00000000 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - Debug - Win32 - - - Release - Win32 - - - - - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} - {AB7EE608-E5FB-42A5-831F-0DEEEA141223} - DUtilUnitTests - ManagedCProj - DynamicLibrary - Unicode - true - false - - - - - - - ..\..\dutil\inc - rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib - - - - - - - - - - - - - - - - - Create - - 4564;4691 - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll - - - ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll - - - - - - {1244E671-F108-4334-BA52-8A7517F26ECD} - - - - - - - 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}. - - - - - \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters deleted file mode 100644 index 4df7af89..00000000 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ /dev/null @@ -1,80 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Resource Files - - - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DictUtilTest.cpp b/src/test/DUtilUnitTest/DictUtilTest.cpp deleted file mode 100644 index 4b4777d7..00000000 --- a/src/test/DUtilUnitTest/DictUtilTest.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -const DWORD numIterations = 100000; - -namespace DutilTests -{ - struct Value - { - DWORD dwNum; - LPWSTR sczKey; - }; - - public ref class DictUtil - { - public: - [Fact] - void DictUtilTest() - { - DutilInitialize(&DutilTestTraceError); - - EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations); - - EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); - - StringListTestHelper(DICT_FLAG_NONE, numIterations); - - StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); - - DutilUninitialize(); - } - - private: - void EmbeddedKeyTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) - { - HRESULT hr = S_OK; - Value *rgValues = NULL; - Value *valueFound = NULL; - DWORD cValues = 0; - LPWSTR sczExpectedKey = NULL; - STRINGDICT_HANDLE sdValues = NULL; - - try - { - hr = DictCreateWithEmbeddedKey(&sdValues, 0, (void **)&rgValues, offsetof(Value, sczKey), dfFlags); - NativeAssert::Succeeded(hr, "Failed to create dictionary of values"); - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - cValues++; - - hr = MemEnsureArraySize((void **)&rgValues, cValues, sizeof(Value), 5); - NativeAssert::Succeeded(hr, "Failed to grow value array"); - - hr = StrAllocFormatted(&rgValues[i].sczKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); - - hr = DictAddValue(sdValues, rgValues + i); - NativeAssert::Succeeded(hr, "Failed to add item {0} to dict", i); - } - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); - - hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - - NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey); - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); - - hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); - - if (dfFlags & DICT_FLAG_CASEINSENSITIVE) - { - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - - NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey, TRUE); - } - else - { - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "This embedded key is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); - } - } - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); - - hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); - } - } - } - finally - { - for (DWORD i = 0; i < cValues; ++i) - { - ReleaseStr(rgValues[i].sczKey); - } - ReleaseMem(rgValues); - ReleaseStr(sczExpectedKey); - ReleaseDict(sdValues); - } - - LExit: - return; - } - - void StringListTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) - { - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - LPWSTR sczExpectedKey = NULL; - STRINGDICT_HANDLE sdValues = NULL; - - try - { - hr = DictCreateStringList(&sdValues, 0, dfFlags); - NativeAssert::Succeeded(hr, "Failed to create dictionary of keys"); - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - hr = StrAllocFormatted(&sczKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); - - hr = DictAddKey(sdValues, sczKey); - NativeAssert::Succeeded(hr, "Failed to add key {0} to dict", i); - } - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); - - hr = DictKeyExists(sdValues, sczExpectedKey); - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); - - hr = DictKeyExists(sdValues, sczExpectedKey); - if (dfFlags & DICT_FLAG_CASEINSENSITIVE) - { - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - } - else - { - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "This stringlist dict is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); - } - } - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); - - hr = DictKeyExists(sdValues, sczExpectedKey); - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); - } - } - } - finally - { - ReleaseStr(sczKey); - ReleaseStr(sczExpectedKey); - ReleaseDict(sdValues); - } - - LExit: - return; - } - }; -} diff --git a/src/test/DUtilUnitTest/DirUtilTests.cpp b/src/test/DUtilUnitTest/DirUtilTests.cpp deleted file mode 100644 index 7643366f..00000000 --- a/src/test/DUtilUnitTest/DirUtilTests.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class DirUtil - { - public: - [Fact] - void DirUtilTest() - { - HRESULT hr = S_OK; - LPWSTR sczCurrentDir = NULL; - LPWSTR sczGuid = NULL; - LPWSTR sczFolder = NULL; - LPWSTR sczSubFolder = NULL; - - try - { - hr = GuidCreate(&sczGuid); - NativeAssert::Succeeded(hr, "Failed to create guid."); - - hr = DirGetCurrent(&sczCurrentDir); - NativeAssert::Succeeded(hr, "Failed to get current directory."); - - hr = PathConcat(sczCurrentDir, sczGuid, &sczFolder); - NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid); - - BOOL fExists = DirExists(sczFolder, NULL); - Assert::False(fExists == TRUE); - - hr = PathConcat(sczFolder, L"foo", &sczSubFolder); - NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder); - - hr = DirEnsureExists(sczSubFolder, NULL); - NativeAssert::Succeeded(hr, "Failed to create multiple directories: %ls", sczSubFolder); - - // Test failure to delete non-empty folder. - hr = DirEnsureDelete(sczFolder, FALSE, FALSE); - Assert::Equal(HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY), hr); - - hr = DirEnsureDelete(sczSubFolder, FALSE, FALSE); - NativeAssert::Succeeded(hr, "Failed to delete single directory: %ls", sczSubFolder); - - // Put the directory back and we'll test deleting tree. - hr = DirEnsureExists(sczSubFolder, NULL); - NativeAssert::Succeeded(hr, "Failed to create single directory: %ls", sczSubFolder); - - hr = DirEnsureDelete(sczFolder, FALSE, TRUE); - NativeAssert::Succeeded(hr, "Failed to delete directory tree: %ls", sczFolder); - - // Finally, try to create "C:\" which would normally fail, but we want success - hr = DirEnsureExists(L"C:\\", NULL); - NativeAssert::Succeeded(hr, "Failed to create C:\\"); - } - finally - { - ReleaseStr(sczSubFolder); - ReleaseStr(sczFolder); - ReleaseStr(sczGuid); - ReleaseStr(sczCurrentDir); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp deleted file mode 100644 index ac071ef2..00000000 --- a/src/test/DUtilUnitTest/FileUtilTest.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class FileUtil - { - public: - [Fact(Skip="Skipped until we have a good way to reference ANSI.txt.")] - void FileUtilTest() - { - HRESULT hr = S_OK; - LPWSTR sczTempDir = NULL; - LPWSTR sczFileDir = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get temp dir"); - - hr = PathExpand(&sczFileDir, L"%WIX_ROOT%\\examples\\data\\TextEncodings\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get path to encodings file dir"); - - hr = DirEnsureExists(sczTempDir, NULL); - NativeAssert::Succeeded(hr, "Failed to ensure directory exists: {0}", sczTempDir); - - TestFile(sczFileDir, sczTempDir, L"ANSI.txt", 32, FILE_ENCODING_UTF8); - // Big endian not supported today! - //TestFile(sczFileDir, L"UnicodeBENoBOM.txt", 34); - //TestFile(sczFileDir, L"UnicodeBEWithBOM.txt", 34); - TestFile(sczFileDir, sczTempDir, L"UnicodeLENoBOM.txt", 34, FILE_ENCODING_UTF16); - TestFile(sczFileDir, sczTempDir, L"UnicodeLEWithBOM.txt", 34, FILE_ENCODING_UTF16_WITH_BOM); - TestFile(sczFileDir, sczTempDir, L"UTF8WithSignature.txt", 34, FILE_ENCODING_UTF8_WITH_BOM); - - hr = DirEnsureDelete(sczTempDir, TRUE, TRUE); - } - finally - { - ReleaseStr(sczTempDir); - ReleaseStr(sczFileDir); - DutilUninitialize(); - } - } - - private: - void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, size_t cbExpectedStringLength, FILE_ENCODING feExpectedEncoding) - { - HRESULT hr = S_OK; - LPWSTR sczFullPath = NULL; - LPWSTR sczContents = NULL; - LPWSTR sczOutputPath = NULL; - FILE_ENCODING feEncodingFound = FILE_ENCODING_UNSPECIFIED; - BYTE *pbFile1 = NULL; - DWORD cbFile1 = 0; - BYTE *pbFile2 = NULL; - DWORD cbFile2 = 0; - size_t cbActualStringLength = 0; - - try - { - hr = PathConcat(wzDir, wzFileName, &sczFullPath); - NativeAssert::Succeeded(hr, "Failed to create path to test file: {0}", sczFullPath); - - hr = FileToString(sczFullPath, &sczContents, &feEncodingFound); - hr = E_FAIL; - NativeAssert::Succeeded(hr, "Failed to read text from file: {0}", sczFullPath); - - if (!sczContents) - { - hr = E_FAIL; - NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath); - } - - hr = ::StringCchLengthW(sczContents, STRSAFE_MAX_CCH, &cbActualStringLength); - NativeAssert::Succeeded(hr, "Failed to get length of text from file: {0}", sczFullPath); - - if (cbActualStringLength != cbExpectedStringLength) - { - hr = E_FAIL; - ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %Iu, found size %Iu)", sczFullPath, cbExpectedStringLength, cbActualStringLength); - } - - if (feEncodingFound != feExpectedEncoding) - { - hr = E_FAIL; - ExitOnFailure(hr, "FileToString() returned unexpected encoding type for file: %ls (expected type %u, found type %u)", sczFullPath, feExpectedEncoding, feEncodingFound); - } - - hr = PathConcat(wzTempDir, wzFileName, &sczOutputPath); - NativeAssert::Succeeded(hr, "Failed to get output path"); - - hr = FileFromString(sczOutputPath, 0, sczContents, feExpectedEncoding); - NativeAssert::Succeeded(hr, "Failed to write contents of file back out to disk"); - - hr = FileRead(&pbFile1, &cbFile1, sczFullPath); - NativeAssert::Succeeded(hr, "Failed to read input file as binary"); - - hr = FileRead(&pbFile2, &cbFile2, sczOutputPath); - NativeAssert::Succeeded(hr, "Failed to read output file as binary"); - - if (cbFile1 != cbFile2 || 0 != memcmp(pbFile1, pbFile2, cbFile1)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Outputted file doesn't match input file: \"%ls\" and \"%ls\"", sczFullPath, sczOutputPath); - } - } - finally - { - ReleaseStr(sczOutputPath); - ReleaseStr(sczFullPath); - ReleaseStr(sczContents); - } - - LExit: - return; - } - }; -} diff --git a/src/test/DUtilUnitTest/GuidUtilTest.cpp b/src/test/DUtilUnitTest/GuidUtilTest.cpp deleted file mode 100644 index a6e27a09..00000000 --- a/src/test/DUtilUnitTest/GuidUtilTest.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class GuidUtil - { - public: - [Fact] - void GuidCreateTest() - { - HRESULT hr = S_OK; - WCHAR wzGuid1[GUID_STRING_LENGTH]; - WCHAR wzGuid2[GUID_STRING_LENGTH]; - - hr = GuidFixedCreate(wzGuid1); - NativeAssert::Succeeded(hr, "Failed to create first guid."); - Guid firstGuid = Guid::Parse(gcnew String(wzGuid1)); - - hr = GuidFixedCreate(wzGuid2); - NativeAssert::Succeeded(hr, "Failed to create second guid."); - Guid secondGuid = Guid::Parse(gcnew String(wzGuid2)); - - NativeAssert::NotStringEqual(wzGuid1, wzGuid2); - NativeAssert::NotEqual(firstGuid, secondGuid); - } - - [Fact] - void GuidCreateSczTest() - { - HRESULT hr = S_OK; - LPWSTR sczGuid1 = NULL; - LPWSTR sczGuid2 = NULL; - - try - { - hr = GuidCreate(&sczGuid1); - NativeAssert::Succeeded(hr, "Failed to create first guid."); - Guid firstGuid = Guid::Parse(gcnew String(sczGuid1)); - - hr = GuidCreate(&sczGuid2); - NativeAssert::Succeeded(hr, "Failed to create second guid."); - Guid secondGuid = Guid::Parse(gcnew String(sczGuid2)); - - NativeAssert::NotStringEqual(sczGuid1, sczGuid2); - NativeAssert::NotEqual(firstGuid, secondGuid); - } - finally - { - ReleaseStr(sczGuid1); - ReleaseStr(sczGuid2); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/IniUtilTest.cpp b/src/test/DUtilUnitTest/IniUtilTest.cpp deleted file mode 100644 index 946f19c5..00000000 --- a/src/test/DUtilUnitTest/IniUtilTest.cpp +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -typedef HRESULT (__clrcall *IniFormatParameters)( - INI_HANDLE - ); - -namespace DutilTests -{ - public ref class IniUtil - { - public: - [Fact] - void IniUtilTest() - { - HRESULT hr = S_OK; - LPWSTR sczTempIniFilePath = NULL; - LPWSTR sczTempIniFileDir = NULL; - 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"; - 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"; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get path to temp INI file"); - - hr = PathGetDirectory(sczTempIniFilePath, &sczTempIniFileDir); - NativeAssert::Succeeded(hr, "Failed to get directory to temp INI file"); - - hr = DirEnsureDelete(sczTempIniFileDir, TRUE, TRUE); - if (E_PATHNOTFOUND == hr) - { - hr = S_OK; - } - NativeAssert::Succeeded(hr, "Failed to delete IniUtilTest directory: {0}", sczTempIniFileDir); - - hr = DirEnsureExists(sczTempIniFileDir, NULL); - NativeAssert::Succeeded(hr, "Failed to ensure temp directory exists: {0}", sczTempIniFileDir); - - // Tests parsing, then modifying a regular INI file - TestReadThenWrite(sczTempIniFilePath, StandardIniFormat, wzIniContents); - - // Tests programmatically creating from scratch, then parsing an INI file - TestWriteThenRead(sczTempIniFilePath, StandardIniFormat); - - // Tests parsing, then modifying a regular INI file - TestReadThenWrite(sczTempIniFilePath, ScriptFormat, wzScriptContents); - - // Tests programmatically creating from scratch, then parsing an INI file - TestWriteThenRead(sczTempIniFilePath, ScriptFormat); - } - finally - { - ReleaseStr(sczTempIniFilePath); - ReleaseStr(sczTempIniFileDir); - DutilUninitialize(); - } - } - - private: - void AssertValue(INI_HANDLE iniHandle, LPCWSTR wzValueName, LPCWSTR wzValue) - { - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - try - { - hr = IniGetValue(iniHandle, wzValueName, &sczValue); - NativeAssert::Succeeded(hr, "Failed to get ini value: {0}", wzValueName); - - if (0 != wcscmp(sczValue, wzValue)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Expected to find value in INI: '%ls'='%ls' - but found value '%ls' instead", wzValueName, wzValue, sczValue); - } - } - finally - { - ReleaseStr(sczValue); - } - - LExit: - return; - } - - void AssertNoValue(INI_HANDLE iniHandle, LPCWSTR wzValueName) - { - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - try - { - hr = IniGetValue(iniHandle, wzValueName, &sczValue); - if (E_NOTFOUND != hr) - { - if (SUCCEEDED(hr)) - { - hr = E_FAIL; - } - ExitOnFailure(hr, "INI value shouldn't have been found: %ls", wzValueName); - } - } - finally - { - ReleaseStr(sczValue); - } - - LExit: - return; - } - - static HRESULT StandardIniFormat(__inout INI_HANDLE iniHandle) - { - HRESULT hr = S_OK; - - hr = IniSetOpenTag(iniHandle, L"[", L"]"); - NativeAssert::Succeeded(hr, "Failed to set open tag settings on ini handle"); - - hr = IniSetValueStyle(iniHandle, NULL, L"="); - NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); - - hr = IniSetCommentStyle(iniHandle, L";"); - NativeAssert::Succeeded(hr, "Failed to set comment style setting on ini handle"); - - return hr; - } - - static HRESULT ScriptFormat(__inout INI_HANDLE iniHandle) - { - HRESULT hr = S_OK; - - hr = IniSetValueStyle(iniHandle, L"setf ~", L" "); - NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); - - return hr; - } - - void TestReadThenWrite(LPWSTR wzIniFilePath, IniFormatParameters SetFormat, LPCWSTR wzContents) - { - HRESULT hr = S_OK; - INI_HANDLE iniHandle = NULL; - INI_HANDLE iniHandle2 = NULL; - INI_VALUE *rgValues = NULL; - DWORD cValues = 0; - - try - { - hr = FileWrite(wzIniFilePath, 0, reinterpret_cast(wzContents), lstrlenW(wzContents) * sizeof(WCHAR), NULL); - NativeAssert::Succeeded(hr, "Failed to write out INI file"); - - hr = IniInitialize(&iniHandle); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniParse(iniHandle, wzIniFilePath, NULL); - NativeAssert::Succeeded(hr, "Failed to parse INI file"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(5, cValues); - - AssertValue(iniHandle, L"PlainValue", L"Blah"); - AssertNoValue(iniHandle, L"PlainValue2"); - AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); - AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); - AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); - AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); - AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); - - hr = IniSetValue(iniHandle, L"PlainValue2", L"Blah2"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(7, cValues); - - AssertValue(iniHandle, L"PlainValue", L"Blah"); - AssertValue(iniHandle, L"PlainValue2", L"Blah2"); - AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); - AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); - AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); - AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); - AssertValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); - AssertValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); - - // Try deleting a value as well - hr = IniSetValue(iniHandle, L"Section1\\Section1ValueB", NULL); - NativeAssert::Succeeded(hr, "Failed to kill value in INI"); - - hr = IniWriteFile(iniHandle, NULL, FILE_ENCODING_UNSPECIFIED); - NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); - - ReleaseNullIni(iniHandle); - // Now re-parse the INI we just wrote and make sure it matches the values we expect - hr = IniInitialize(&iniHandle2); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle2); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniParse(iniHandle2, wzIniFilePath, NULL); - NativeAssert::Succeeded(hr, "Failed to parse INI file"); - - hr = IniGetValueList(iniHandle2, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(6, cValues); - - AssertValue(iniHandle2, L"PlainValue", L"Blah"); - AssertValue(iniHandle2, L"PlainValue2", L"Blah2"); - AssertValue(iniHandle2, L"Section1\\Section1ValueA", L"Foo"); - AssertNoValue(iniHandle2, L"Section1\\Section1ValueB"); - AssertValue(iniHandle2, L"Section2\\Section2ValueA", L"Cha"); - AssertNoValue(iniHandle2, L"Section1\\ValueDoesntExist"); - AssertValue(iniHandle2, L"Section1\\CreatedValue", L"Woo"); - AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arrmod"); - } - finally - { - ReleaseIni(iniHandle); - ReleaseIni(iniHandle2); - } - } - - void TestWriteThenRead(LPWSTR wzIniFilePath, IniFormatParameters SetFormat) - { - HRESULT hr = S_OK; - INI_HANDLE iniHandle = NULL; - INI_HANDLE iniHandle2 = NULL; - INI_VALUE *rgValues = NULL; - DWORD cValues = 0; - - try - { - hr = FileEnsureDelete(wzIniFilePath); - NativeAssert::Succeeded(hr, "Failed to ensure file is deleted"); - - hr = IniInitialize(&iniHandle); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(0, cValues); - - hr = IniSetValue(iniHandle, L"Value1", L"BlahTypo"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value2", L"Blah2"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arr"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value3", L"Blah3"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value4", L"Blah4"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value4", NULL); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value1", L"Blah1"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(8, cValues); - - AssertValue(iniHandle, L"Value1", L"Blah1"); - AssertValue(iniHandle, L"Value2", L"Blah2"); - AssertValue(iniHandle, L"Value3", L"Blah3"); - AssertNoValue(iniHandle, L"Value4"); - AssertValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); - AssertValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); - AssertValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); - AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); - - hr = IniWriteFile(iniHandle, wzIniFilePath, FILE_ENCODING_UNSPECIFIED); - NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); - - ReleaseNullIni(iniHandle); - // Now re-parse the INI we just wrote and make sure it matches the values we expect - hr = IniInitialize(&iniHandle2); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle2); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniParse(iniHandle2, wzIniFilePath, NULL); - NativeAssert::Succeeded(hr, "Failed to parse INI file"); - - hr = IniGetValueList(iniHandle2, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(7, cValues); - - AssertValue(iniHandle2, L"Value1", L"Blah1"); - AssertValue(iniHandle2, L"Value2", L"Blah2"); - AssertValue(iniHandle2, L"Value3", L"Blah3"); - AssertNoValue(iniHandle2, L"Value4"); - AssertValue(iniHandle2, L"Section1\\Value1", L"Section1Value1"); - AssertValue(iniHandle2, L"Section1\\Value2", L"Section1Value2"); - AssertValue(iniHandle2, L"Section2\\Value1", L"Section2Value1"); - AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arr"); - } - finally - { - ReleaseIni(iniHandle); - ReleaseIni(iniHandle2); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/MemUtilTest.cpp b/src/test/DUtilUnitTest/MemUtilTest.cpp deleted file mode 100644 index 09692bfb..00000000 --- a/src/test/DUtilUnitTest/MemUtilTest.cpp +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - struct ArrayValue - { - DWORD dwNum; - void *pvNull1; - LPWSTR sczString; - void *pvNull2; - }; - - public ref class MemUtil - { - public: - [Fact] - void MemUtilAppendTest() - { - HRESULT hr = S_OK; - DWORD dwSize; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 1"); - ++cValues; - SetItem(rgValues + 0, 0); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 2"); - ++cValues; - SetItem(rgValues + 1, 1); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 3"); - ++cValues; - SetItem(rgValues + 2, 2); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 4"); - ++cValues; - SetItem(rgValues + 3, 3); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 5"); - ++cValues; - SetItem(rgValues + 4, 4); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 6"); - ++cValues; - SetItem(rgValues + 5, 5); - - // OK, we used growth size 5, so let's try ensuring we have space for 6 (5 + first item) items - // and make sure it doesn't grow since we already have enough space - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to ensure array size matches what it should already be"); - dwSize = MemSize(rgValues); - if (dwSize != 6 * sizeof(ArrayValue)) - { - hr = E_FAIL; - ExitOnFailure(hr, "MemEnsureArraySize is growing an array that is already big enough!"); - } - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - SetItem(rgValues + 6, 6); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - SetItem(rgValues + 7, 7); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - SetItem(rgValues + 8, 8); - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - } - finally - { - ReleaseMem(rgValues); - } - - LExit: - DutilUninitialize(); - } - - [Fact] - void MemUtilInsertTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of empty array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 5); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert at end of array"); - ++cValues; - CheckNullItem(rgValues + 1); - SetItem(rgValues + 1, 6); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 4); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 3); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 1); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 1); - SetItem(rgValues + 1, 2); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 0); - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - CheckNullItem(rgValues + 7); - SetItem(rgValues + 7, 7); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 8, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 8); - SetItem(rgValues + 8, 8); - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - [Fact] - void MemUtilRemovePreserveOrderTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); - NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); - - cValues = 10; - for (DWORD i = 0; i < cValues; ++i) - { - SetItem(rgValues + i, i); - } - - // Remove last item - MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), TRUE); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove last two items - MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove first item - MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), TRUE); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i + 1); - } - - - // Remove first two items - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i + 3); - } - - // Remove middle two items - MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - - CheckItem(rgValues, 3); - CheckItem(rgValues + 1, 6); - - // Remove last 2 items to ensure we don't crash - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - [Fact] - void MemUtilRemoveFastTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); - NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); - - cValues = 10; - for (DWORD i = 0; i < cValues; ++i) - { - SetItem(rgValues + i, i); - } - - // Remove last item - MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), FALSE); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove last two items - MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove first item - MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), FALSE); - --cValues; - - CheckItem(rgValues, 6); - CheckItem(rgValues + 1, 1); - CheckItem(rgValues + 2, 2); - CheckItem(rgValues + 3, 3); - CheckItem(rgValues + 4, 4); - CheckItem(rgValues + 5, 5); - - // Remove first two items - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - - CheckItem(rgValues, 4); - CheckItem(rgValues + 1, 5); - CheckItem(rgValues + 2, 2); - CheckItem(rgValues + 3, 3); - - - // Remove middle two items - MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - - CheckItem(rgValues, 4); - CheckItem(rgValues + 1, 3); - - // Remove last 2 items to ensure we don't crash - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - [Fact] - void MemUtilSwapTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); - NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); - - cValues = 10; - for (DWORD i = 0; i < cValues; ++i) - { - SetItem(rgValues + i, i); - } - - // Swap first two - MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 1); - CheckItem(rgValues + 1, 0); - for (DWORD i = 2; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap them back - MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap first and last items (index 0 and 9) - MemArraySwapItems(rgValues, 0, 9, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 9); - CheckItem(rgValues + 9, 0); - for (DWORD i = 1; i < cValues - 1; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap index 1 and 8 - MemArraySwapItems(rgValues, 1, 8, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 9); - CheckItem(rgValues + 1, 8); - CheckItem(rgValues + 8, 1); - CheckItem(rgValues + 9, 0); - for (DWORD i = 2; i < cValues - 2; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap index 2 and 7 - MemArraySwapItems(rgValues, 2, 7, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 9); - CheckItem(rgValues + 1, 8); - CheckItem(rgValues + 2, 7); - CheckItem(rgValues + 7, 2); - CheckItem(rgValues + 8, 1); - CheckItem(rgValues + 9, 0); - for (DWORD i = 3; i < cValues - 3; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap index 0 and 1 - MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 8); - CheckItem(rgValues + 1, 9); - CheckItem(rgValues + 2, 7); - CheckItem(rgValues + 7, 2); - CheckItem(rgValues + 8, 1); - CheckItem(rgValues + 9, 0); - for (DWORD i = 3; i < cValues - 3; ++i) - { - CheckItem(rgValues + i, i); - } - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - private: - void SetItem(ArrayValue *pValue, DWORD dwValue) - { - HRESULT hr = S_OK; - pValue->dwNum = dwValue; - - hr = StrAllocFormatted(&pValue->sczString, L"%u", dwValue); - NativeAssert::Succeeded(hr, "Failed to allocate string"); - } - - void CheckItem(ArrayValue *pValue, DWORD dwValue) - { - HRESULT hr = S_OK; - LPWSTR sczTemp = NULL; - - try - { - NativeAssert::Equal(dwValue, pValue->dwNum); - - hr = StrAllocFormatted(&sczTemp, L"%u", dwValue); - NativeAssert::Succeeded(hr, "Failed to allocate temp string"); - - NativeAssert::StringEqual(sczTemp, pValue->sczString, TRUE); - - if (pValue->pvNull1 || pValue->pvNull2) - { - hr = E_FAIL; - ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); - } - } - finally - { - ReleaseStr(sczTemp); - } - - LExit: - return; - } - - void CheckNullItem(ArrayValue *pValue) - { - HRESULT hr = S_OK; - - NativeAssert::Equal(0, pValue->dwNum); - - if (pValue->sczString) - { - hr = E_FAIL; - ExitOnFailure(hr, "Item found isn't NULL!"); - } - - if (pValue->pvNull1 || pValue->pvNull2) - { - hr = E_FAIL; - ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); - } - - LExit: - return; - } - }; -} diff --git a/src/test/DUtilUnitTest/MonUtilTest.cpp b/src/test/DUtilUnitTest/MonUtilTest.cpp deleted file mode 100644 index 273f2eb6..00000000 --- a/src/test/DUtilUnitTest/MonUtilTest.cpp +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#undef RemoveDirectory - -using namespace System; -using namespace System::Collections::Generic; -using namespace System::Runtime::InteropServices; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - const int PREWAIT = 20; - const int POSTWAIT = 480; - const int FULLWAIT = 500; - const int SILENCEPERIOD = 100; - - struct RegKey - { - HRESULT hr; - HKEY hkRoot; - LPCWSTR wzSubKey; - REG_KEY_BITNESS kbKeyBitness; - BOOL fRecursive; - }; - struct Directory - { - HRESULT hr; - LPCWSTR wzPath; - BOOL fRecursive; - }; - struct Results - { - RegKey *rgRegKeys; - DWORD cRegKeys; - Directory *rgDirectories; - DWORD cDirectories; - }; - - public delegate void MonGeneralDelegate(HRESULT, LPVOID); - - public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID); - - public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID); - - public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID); - - static void MonGeneral( - __in HRESULT /*hrResult*/, - __in_opt LPVOID /*pvContext*/ - ) - { - Assert::True(false); - } - - static void MonDriveStatus( - __in WCHAR /*chDrive*/, - __in BOOL /*fArriving*/, - __in_opt LPVOID /*pvContext*/ - ) - { - } - - static void MonDirectory( - __in HRESULT hrResult, - __in_z LPCWSTR wzPath, - __in_z BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvDirectoryContext - ) - { - Assert::Equal(S_OK, hrResult); - Assert::Equal(0, reinterpret_cast(pvDirectoryContext)); - - HRESULT hr = S_OK; - Results *pResults = reinterpret_cast(pvContext); - - hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5); - NativeAssert::ValidReturnCode(hr, S_OK); - ++pResults->cDirectories; - - pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult; - pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath; - pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive; - } - - static void MonRegKey( - __in HRESULT hrResult, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in_z BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvRegKeyContext - ) - { - Assert::Equal(S_OK, hrResult); - Assert::Equal(0, reinterpret_cast(pvRegKeyContext)); - - HRESULT hr = S_OK; - Results *pResults = reinterpret_cast(pvContext); - - hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5); - NativeAssert::ValidReturnCode(hr, S_OK); - ++pResults->cRegKeys; - - pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult; - pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot; - pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey; - pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness; - pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive; - } - - public ref class MonUtil - { - public: - void ClearResults(Results *pResults) - { - ReleaseNullMem(pResults->rgDirectories); - pResults->cDirectories = 0; - ReleaseNullMem(pResults->rgRegKeys); - pResults->cRegKeys = 0; - } - - void RemoveDirectory(LPCWSTR wzPath) - { - DWORD dwRetryCount = 0; - const DWORD c_dwMaxRetryCount = 100; - const DWORD c_dwRetryInterval = 50; - - HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE); - - // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed - // (and deletion will be "pending" until our monitor handle is closed) - // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete() - // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later - // (after the waiter thread wakes up, it will release the handle) - while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount) - { - ::Sleep(c_dwRetryInterval); - ++dwRetryCount; - hr = DirEnsureDelete(wzPath, TRUE, TRUE); - } - - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); - } - - void TestDirectory(MON_HANDLE handle, Results *pResults) - { - HRESULT hr = S_OK; - LPWSTR sczShallowPath = NULL; - LPWSTR sczParentPath = NULL; - LPWSTR sczDeepPath = NULL; - LPWSTR sczChildPath = NULL; - LPWSTR sczChildFilePath = NULL; - - try - { - hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - RemoveDirectory(sczShallowPath); - - hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = DirEnsureExists(sczParentPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - // Make sure creating the parent directory does nothing, even after silence period - ::Sleep(FULLWAIT); - Assert::Equal(0, pResults->cDirectories); - - // Now create the target path, no notification until after the silence period - hr = DirEnsureExists(sczDeepPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(PREWAIT); - Assert::Equal(0, pResults->cDirectories); - - // Now after the full silence period, it should have triggered - ::Sleep(POSTWAIT); - Assert::Equal(1, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK); - - // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. - RemoveDirectory(sczShallowPath); - - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK); - - // Create the parent directory again, still should be nothing even after full silence period - hr = DirEnsureExists(sczParentPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cDirectories); - - hr = DirEnsureExists(sczChildPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(PREWAIT); - Assert::Equal(2, pResults->cDirectories); - - ::Sleep(POSTWAIT); - Assert::Equal(3, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); - - // Write a file to a deep child subfolder, and make sure it's detected - hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - ::Sleep(PREWAIT); - Assert::Equal(3, pResults->cDirectories); - - ::Sleep(POSTWAIT); - Assert::Equal(4, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); - - RemoveDirectory(sczParentPath); - - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); - - // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked - hr = MonRemoveDirectory(handle, sczDeepPath, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK); - ::Sleep(PREWAIT); - - hr = DirEnsureExists(sczDeepPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); - - // Finally, add it back so we can test multiple things to monitor at once - hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - } - finally - { - ReleaseStr(sczShallowPath); - ReleaseStr(sczDeepPath); - ReleaseStr(sczParentPath); - } - } - - void TestRegKey(MON_HANDLE handle, Results *pResults) - { - HRESULT hr = S_OK; - LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\"; - LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\"; - LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\"; - LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\"; - HKEY hk = NULL; - - try - { - hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); - - hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - ReleaseRegKey(hk); - // Make sure creating the parent key does nothing, even after silence period - ::Sleep(FULLWAIT); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - Assert::Equal(0, pResults->cRegKeys); - - // Now create the target path, no notification until after the silence period - hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ReleaseRegKey(hk); - ::Sleep(PREWAIT); - Assert::Equal(0, pResults->cRegKeys); - - // Now after the full silence period, it should have triggered - ::Sleep(POSTWAIT); - Assert::Equal(1, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK); - - // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. - hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); - ::Sleep(PREWAIT); - Assert::Equal(1, pResults->cRegKeys); - - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK); - - // Create the parent directory again, still should be nothing even after full silence period - hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ReleaseRegKey(hk); - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cRegKeys); - - hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(PREWAIT); - Assert::Equal(2, pResults->cRegKeys); - - ::Sleep(FULLWAIT); - Assert::Equal(3, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); - - // Write a registry value to some deep child subkey, and make sure it's detected - hr = RegWriteString(hk, L"valuename", L"testvalue"); - NativeAssert::ValidReturnCode(hr, S_OK); - ReleaseRegKey(hk); - ::Sleep(PREWAIT); - Assert::Equal(3, pResults->cRegKeys); - - ::Sleep(FULLWAIT); - Assert::Equal(4, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); - - hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cRegKeys); - - // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked - hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ReleaseRegKey(hk); - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cRegKeys); - } - finally - { - ReleaseRegKey(hk); - } - } - - void TestMoreThan64(MON_HANDLE handle, Results *pResults) - { - HRESULT hr = S_OK; - LPWSTR sczBaseDir = NULL; - LPWSTR sczDir = NULL; - LPWSTR sczFile = NULL; - - try - { - hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - for (DWORD i = 0; i < 200; ++i) - { - hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = DirEnsureExists(sczDir, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - - hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - } - - hr = PathConcat(sczDir, L"file.txt", &sczFile); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(1, pResults->cDirectories); - - for (DWORD i = 0; i < 199; ++i) - { - hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = MonRemoveDirectory(handle, sczDir, FALSE); - NativeAssert::ValidReturnCode(hr, S_OK); - } - ::Sleep(FULLWAIT); - - hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cDirectories); - - for (DWORD i = 0; i < 199; ++i) - { - hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - } - ::Sleep(FULLWAIT); - - hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(3, pResults->cDirectories); - } - finally - { - ReleaseStr(sczBaseDir); - ReleaseStr(sczDir); - ReleaseStr(sczFile); - } - } - - [Fact(Skip = "Test demonstrates failure")] - void MonUtilTest() - { - HRESULT hr = S_OK; - MON_HANDLE handle = NULL; - List^ gcHandles = gcnew List(); - Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE); - Assert::True(NULL != pResults); - - try - { - // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild - MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral); - GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral); - gcHandles->Add(gchMonGeneral); - IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral); - - MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus); - GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus); - gcHandles->Add(gchMonDriveStatus); - IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus); - - MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory); - GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory); - gcHandles->Add(gchMonDirectory); - IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory); - - MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey); - GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey); - gcHandles->Add(gchMonRegKey); - IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey); - - // "Silence period" is 100 ms - hr = MonCreate(&handle, static_cast(ipMonGeneral.ToPointer()), static_cast(ipMonDriveStatus.ToPointer()), static_cast(ipMonDirectory.ToPointer()), static_cast(ipMonRegKey.ToPointer()), pResults); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = RegInitialize(); - NativeAssert::ValidReturnCode(hr, S_OK); - - TestDirectory(handle, pResults); - ClearResults(pResults); - TestRegKey(handle, pResults); - ClearResults(pResults); - TestMoreThan64(handle, pResults); - ClearResults(pResults); - } - finally - { - ReleaseMon(handle); - - for each (GCHandle gcHandle in gcHandles) - { - gcHandle.Free(); - } - - ReleaseMem(pResults->rgDirectories); - ReleaseMem(pResults->rgRegKeys); - ReleaseMem(pResults); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/PathUtilTest.cpp b/src/test/DUtilUnitTest/PathUtilTest.cpp deleted file mode 100644 index 5a1f06fd..00000000 --- a/src/test/DUtilUnitTest/PathUtilTest.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class PathUtil - { - public: - [Fact] - void PathGetHierarchyArrayTest() - { - HRESULT hr = S_OK; - LPWSTR *rgsczPaths = NULL; - UINT cPaths = 0; - - try - { - hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\a.txt", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path"); - Assert::Equal(5, cPaths); - NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\a.txt", rgsczPaths[4]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path"); - Assert::Equal(4, cPaths); - NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\file.txt", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path"); - Assert::Equal(3, cPaths); - NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\file.txt", rgsczPaths[2]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); - Assert::Equal(2, cPaths); - NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\ValueName", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); - Assert::Equal(4, cPaths); - NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); - NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\ValueName", rgsczPaths[3]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); - Assert::Equal(3, cPaths); - NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); - ReleaseNullStrArray(rgsczPaths, cPaths); - } - finally - { - ReleaseStrArray(rgsczPaths, cPaths); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/SceUtilTest.cpp b/src/test/DUtilUnitTest/SceUtilTest.cpp deleted file mode 100644 index 75b9222a..00000000 --- a/src/test/DUtilUnitTest/SceUtilTest.cpp +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#include -#include - -using namespace System; -using namespace Xunit; -using namespace WixTest; - -#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);}; - -namespace DutilTests -{ - enum TABLES - { - TABLE_A, - TABLE_COUNT - }; - - enum TABLE_A_COLUMNS - { - TABLE_A_KEY, - TABLE_A_BINARY, - TABLE_A_DWORD, - TABLE_A_QWORD, - TABLE_A_BOOL, - TABLE_A_STRING, - TABLE_A_DWORD_NULLABLE, - TABLE_A_INITIAL_COLUMNS, - - TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS, - TABLE_A_FINAL_COLUMNS - }; - - struct TableARowValue - { - DWORD dwAutoGenKey; - - BYTE *pbBinary; - DWORD cBinary; - - DWORD dw; - DWORD64 qw; - BOOL f; - LPWSTR scz; - - BOOL fNullablePresent; - DWORD dwNullable; - - BOOL fSchemaV2; - LPWSTR sczExtra; - }; - - public ref class SceUtil - { - public: - void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema) - { - DWORD dwTable; - - for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns); - ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes); - } - - ReleaseMem(pdsSchema->rgTables); - - return; - } - - void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended) - { - pSchema->cTables = TABLE_COUNT; - pSchema->rgTables = static_cast(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE)); - NativeAssert::True(pSchema->rgTables != NULL); - - pSchema->rgTables[TABLE_A].wzName = L"TableA"; - pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS; - pSchema->rgTables[TABLE_A].cIndexes = 2; - - for (DWORD i = 0; i < pSchema->cTables; ++i) - { - pSchema->rgTables[i].rgColumns = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE)); - NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL); - - pSchema->rgTables[i].rgIndexes = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE)); - NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL); - } - - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE; - - if (fIncludeExtended) - { - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE; - } - - static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD }; - static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING }; - - ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword"); - ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String"); - } - - void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra) - { - pValue->pbBinary = pbBinary; - pValue->cBinary = cBinary; - pValue->dw = dw; - pValue->qw = qw; - pValue->f = f; - pValue->scz = scz; - - if (pdw) - { - pValue->fNullablePresent = TRUE; - pValue->dwNullable = *pdw; - } - else - { - pValue->fNullablePresent = FALSE; - } - - if (sczExtra) - { - pValue->fSchemaV2 = TRUE; - pValue->sczExtra = sczExtra; - } - else - { - pValue->fSchemaV2 = FALSE; - } - } - - void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther) - { - NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary); - NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary)); - - NativeAssert::Equal(pValueExpected->dw, pValueOther->dw); - NativeAssert::Equal(pValueExpected->qw, pValueOther->qw); - NativeAssert::Equal(pValueExpected->f, pValueOther->f); - NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz)); - - NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent); - if (pValueExpected->fNullablePresent) - { - NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable); - } - - NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2); - if (pValueExpected->fSchemaV2) - { - NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra)); - } - } - - void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback) - { - HRESULT hr = S_OK; - SCE_ROW_HANDLE sceRow = NULL; - - hr = SceBeginTransaction(pDatabase); - NativeAssert::Succeeded(hr, "Failed to begin transaction"); - - hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow); - NativeAssert::Succeeded(hr, "Failed to prepare to insert row"); - - hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary); - NativeAssert::Succeeded(hr, "Failed to set binary value"); - - hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw); - NativeAssert::Succeeded(hr, "Failed to set dword value"); - - hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw); - NativeAssert::Succeeded(hr, "Failed to set qword value"); - - hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f); - NativeAssert::Succeeded(hr, "Failed to set bool value"); - - hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz); - NativeAssert::Succeeded(hr, "Failed to set string value"); - - if (pValue->fNullablePresent) - { - hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable); - NativeAssert::Succeeded(hr, "Failed to set dword value"); - } - else - { - hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE); - NativeAssert::Succeeded(hr, "Failed to set null value"); - } - - if (pValue->fSchemaV2) - { - hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra); - NativeAssert::Succeeded(hr, "Failed to set extra string value"); - } - - hr = SceFinishUpdate(sceRow); - NativeAssert::Succeeded(hr, "Failed to finish insert"); - - if (fRollback) - { - hr = SceRollbackTransaction(pDatabase); - NativeAssert::Succeeded(hr, "Failed to rollback transaction"); - } - else - { - hr = SceCommitTransaction(pDatabase); - NativeAssert::Succeeded(hr, "Failed to commit transaction"); - - hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey); - NativeAssert::Succeeded(hr, "Failed to get autogen key after insert"); - - NativeAssert::True(pValue->dwAutoGenKey != 0); - } - - ReleaseSceRow(sceRow); - } - - void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow) - { - HRESULT hr = S_OK; - TableARowValue value = {}; - - hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary); - NativeAssert::Succeeded(hr, "Failed to get binary value from result row"); - - hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw); - NativeAssert::Succeeded(hr, "Failed to get dword value from result row"); - - hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw); - NativeAssert::Succeeded(hr, "Failed to get qword value from result row"); - - hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f); - NativeAssert::Succeeded(hr, "Failed to get bool value from result row"); - - hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz); - NativeAssert::Succeeded(hr, "Failed to get string value from result row"); - - hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable); - if (hr == E_NOTFOUND) - { - value.fNullablePresent = FALSE; - hr = S_OK; - } - else - { - NativeAssert::Succeeded(hr, "Failed to get string value from result row"); - value.fNullablePresent = TRUE; - } - - if (pExpectedValue->fSchemaV2) - { - value.fSchemaV2 = TRUE; - hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra); - NativeAssert::Succeeded(hr, "Failed to get extra string value from result row"); - } - - AssertStructValuesSame(pExpectedValue, &value); - - ReleaseNullMem(value.pbBinary); - ReleaseNullStr(value.scz); - } - - void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults) - { - HRESULT hr = S_OK; - SCE_ROW_HANDLE sceRow = NULL; - - for (DWORD i = 0; i < cExpectedValues; ++i) - { - hr = SceGetNextResultRow(queryResults, &sceRow); - NativeAssert::Succeeded(hr, "Failed to get next result row"); - - VerifyRow(rgExpectedValues[i], sceRow); - ReleaseNullSceRow(sceRow); - } - - // No more results - NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow))); - } - - void TestIndex(SCE_DATABASE *pDatabase) - { - HRESULT hr = S_OK; - BYTE binary1[50] = { 0x80, 0x70 }; - BYTE binary2[40] = { 0x90, 0xAB }; - BYTE binary3[40] = { 0x85, 0x88 }; - DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888; - TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {}; - SCE_QUERY_HANDLE query = NULL; - SCE_QUERY_RESULTS_HANDLE results = NULL; - - SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL); - SetStructValues(&value2, static_cast(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL); - SetStructValues(&value3, static_cast(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL); - SetStructValues(&value4, static_cast(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL); - SetStructValues(&value5, static_cast(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL); - - // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards - InsertRow(pDatabase, &value1, TRUE); - - InsertRow(pDatabase, &value1, FALSE); - InsertRow(pDatabase, &value2, FALSE); - InsertRow(pDatabase, &value3, FALSE); - InsertRow(pDatabase, &value4, FALSE); - InsertRow(pDatabase, &value5, FALSE); - - NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey); - - // Test setting 1 column - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 }; - VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results); - ReleaseNullSceQueryResults(results); - - // Test setting 2 columns, third column is unspecified so results are sorted by it - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceSetQueryColumnString(query, L"yyy"); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery2[] = { &value5, &value2 }; - VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results); - ReleaseNullSceQueryResults(results); - - // Test setting 2 columns, third column of index is unspecified so results are sorted by it - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceSetQueryColumnString(query, L"yyy"); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery3[] = { &value5, &value2 }; - VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results); - ReleaseNullSceQueryResults(results); - - // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by - hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceSetQueryColumnString(query, L"yyy"); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery4[] = { &value2, &value5 }; - VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results); - ReleaseNullSceQueryResults(results); - } - - void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase) - { - HRESULT hr = S_OK; - BYTE binary1[40] = { 0x55, 0x44 }; - DWORD dwValue1 = 58; - TableARowValue value1 = {}; - SCE_QUERY_HANDLE query = NULL; - SCE_ROW_HANDLE row = NULL; - - SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring"); - - InsertRow(pDatabase, &value1, FALSE); - - // Test setting 1 column - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 5); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryExact(&query, &row); - NativeAssert::Succeeded(hr, "Failed to run query exact"); - - VerifyRow(&value1, row); - } - - [Fact] - void SceUtilTest() - { - HRESULT hr = S_OK; - BOOL fComInitialized = FALSE; - LPWSTR sczDbPath = NULL; - SCE_DATABASE *pDatabase = NULL; - SCE_DATABASE_SCHEMA schema1 = {}; - SCE_DATABASE_SCHEMA schema2 = {}; - - try - { - hr = ::CoInitialize(0); - NativeAssert::Succeeded(hr, "Failed to initialize COM"); - fComInitialized = TRUE; - - SetupSchema(&schema1, FALSE); - SetupSchema(&schema2, TRUE); - - hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get path to test database"); - - FileEnsureDelete(sczDbPath); - - hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase); - NativeAssert::Succeeded(hr, "Failed to ensure database schema"); - - TestIndex(pDatabase); - - hr = SceCloseDatabase(pDatabase); - pDatabase = NULL; - NativeAssert::Succeeded(hr, "Failed to close database"); - - // Add column to schema - hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase); - NativeAssert::Succeeded(hr, "Failed to ensure database schema"); - - TestReadWriteSchemaV2(pDatabase); - } - finally - { - ReleaseSceSchema(&schema1); - ReleaseSceSchema(&schema2); - - if (NULL != pDatabase) - { - hr = SceCloseDatabase(pDatabase); - NativeAssert::Succeeded(hr, "Failed to close database"); - } - ReleaseStr(sczDbPath); - - if (fComInitialized) - { - ::CoUninitialize(); - } - } - } - }; -} diff --git a/src/test/DUtilUnitTest/StrUtilTest.cpp b/src/test/DUtilUnitTest/StrUtilTest.cpp deleted file mode 100644 index 94fee280..00000000 --- a/src/test/DUtilUnitTest/StrUtilTest.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class StrUtil - { - public: - [Fact] - void StrUtilFormattedTest() - { - HRESULT hr = S_OK; - LPWSTR sczText = NULL; - - try - { - hr = StrAllocFormatted(&sczText, L"%hs - %ls - %u", "ansi string", L"unicode string", 1234); - NativeAssert::Succeeded(hr, "Failed to format string."); - NativeAssert::StringEqual(L"ansi string - unicode string - 1234", sczText); - - ReleaseNullStr(sczText); - - hr = StrAllocString(&sczText, L"repeat", 0); - NativeAssert::Succeeded(hr, "Failed to allocate string."); - - hr = StrAllocFormatted(&sczText, L"%ls and %ls", sczText, sczText); - NativeAssert::Succeeded(hr, "Failed to format string unto itself."); - NativeAssert::StringEqual(L"repeat and repeat", sczText); - } - finally - { - ReleaseStr(sczText); - } - } - - [Fact] - void StrUtilTrimTest() - { - TestTrim(L"", L""); - TestTrim(L"Blah", L"Blah"); - TestTrim(L"\t\t\tBlah", L"Blah"); - TestTrim(L"\t Blah ", L"Blah"); - TestTrim(L"Blah ", L"Blah"); - TestTrim(L"\t Spaces \t Between \t", L"Spaces \t Between"); - TestTrim(L" \t\t\t ", L""); - - TestTrimAnsi("", ""); - TestTrimAnsi("Blah", "Blah"); - TestTrimAnsi("\t\t\tBlah", "Blah"); - TestTrimAnsi(" Blah ", "Blah"); - TestTrimAnsi("Blah ", "Blah"); - TestTrimAnsi("\t Spaces \t Between \t", "Spaces \t Between"); - TestTrimAnsi(" \t\t\t ", ""); - } - - [Fact] - void StrUtilConvertTest() - { - char a[] = { 'a', 'b', 'C', 'd', '\0', '\0' }; - - TestStrAllocStringAnsi(a, 5, L"abCd"); - TestStrAllocStringAnsi(a, 4, L"abCd"); - TestStrAllocStringAnsi(a, 3, L"abC"); - TestStrAllocStringAnsi(a, 2, L"ab"); - TestStrAllocStringAnsi(a, 1, L"a"); - TestStrAllocStringAnsi(a, 0, L"abCd"); - - wchar_t b[] = { L'a', L'b', L'C', L'd', L'\0', L'\0' }; - - TestStrAnsiAllocString(b, 5, "abCd"); - TestStrAnsiAllocString(b, 4, "abCd"); - TestStrAnsiAllocString(b, 3, "abC"); - TestStrAnsiAllocString(b, 2, "ab"); - TestStrAnsiAllocString(b, 1, "a"); - TestStrAnsiAllocString(b, 0, "abCd"); - } - - private: - void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult) - { - HRESULT hr = S_OK; - LPWSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrTrimWhitespace(&sczOutput, wzInput); - NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: {0}", wzInput); - - if (0 != wcscmp(wzExpectedResult, sczOutput)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Trimmed string \"%ls\", expected result \"%ls\", actual result \"%ls\"", wzInput, wzExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - - void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult) - { - HRESULT hr = S_OK; - LPSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrAnsiTrimWhitespace(&sczOutput, szInput); - NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: \"{0}\"", szInput); - - if (0 != strcmp(szExpectedResult, sczOutput)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Trimmed string \"%hs\", expected result \"%hs\", actual result \"%hs\"", szInput, szExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - - void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult) - { - HRESULT hr = S_OK; - LPWSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8); - NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", szSource); - - if (0 != wcscmp(sczOutput, wzExpectedResult)) - { - hr = E_FAIL; - ExitOnFailure(hr, "String doesn't match, expected result \"%ls\", actual result \"%ls\"", wzExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - - void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult) - { - HRESULT hr = S_OK; - LPSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8); - NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", wzSource); - - if (0 != strcmp(sczOutput, szExpectedResult)) - { - hr = E_FAIL; - ExitOnFailure(hr, "String doesn't match, expected result \"%hs\", actual result \"%hs\"", szExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - }; -} diff --git a/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml b/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml deleted file mode 100644 index d9f961fe..00000000 --- a/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - BundleB v2.0 - Bundle Subtitle. - 1116353B-7C6E-4C29-BFA1-D4A972CD421D - 2014-07-14T12:39:00.000Z - http://localhost:9999/wix4/BundleB/feed - - manual build - - Bundle v2.0 - v2.0 - - Bundle_Author - http://mycompany.com/software - Bundle_Author@mycompany.com - - - - - <p>Change list:</p><ul> - <li>Updated release.</li> - </ul> - - 2.0.0.0 - 2014-11-10T12:39:00.000Z - - - Bundle v1.0 - v1.0 - - Bundle_Author - http://mycompany.com/software - Bundle_Author@mycompany.com - - - - - <p>Change list:</p><ul> - <li>Initial release.</li> - </ul> - - - 1.0.0.0 - 2014-11-09T12:39:00.000Z - - - Bundle v1.0-preview - v1.0-preview - - Bundle_Author - http://mycompany.com/software - Bundle_Author@mycompany.com - - - - - <p>Change list:</p><ul> - <li>Initial release.</li> - </ul> - - 1.0.0.0 - 2014-11-09T12:39:00.000Z - - diff --git a/src/test/DUtilUnitTest/UnitTest.rc b/src/test/DUtilUnitTest/UnitTest.rc deleted file mode 100644 index 14cebe1a..00000000 --- a/src/test/DUtilUnitTest/UnitTest.rc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define VER_APP -#define VER_ORIGINAL_FILENAME "UnitTest.dll" -#define VER_INTERNAL_NAME "setup" -#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" diff --git a/src/test/DUtilUnitTest/UriUtilTest.cpp b/src/test/DUtilUnitTest/UriUtilTest.cpp deleted file mode 100644 index b3bf87a2..00000000 --- a/src/test/DUtilUnitTest/UriUtilTest.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace System::Text; -using namespace System::Collections::Generic; -using namespace Xunit; - -namespace CfgTests -{ - public ref class UriUtil - { - public: - [Fact] - void UriProtocolTest() - { - HRESULT hr = S_OK; - - DutilInitialize(&DutilTestTraceError); - - LPCWSTR uri = L"https://localhost/"; - URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); - - uri = L"HTTPS://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); - - uri = L"HtTpS://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); - - uri = L"HTTP://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); - - uri = L"http://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); - - uri = L"HtTp://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); - - uri = L"file://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); - - uri = L"FILE://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); - - uri = L"FiLe://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); - - uri = L"FTP://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); - - uri = L"ftp://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); - - uri = L"FtP://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); - - LExit: - DutilUninitialize(); - } - }; -} diff --git a/src/test/DUtilUnitTest/VerUtilTests.cpp b/src/test/DUtilUnitTest/VerUtilTests.cpp deleted file mode 100644 index 8f24ad1a..00000000 --- a/src/test/DUtilUnitTest/VerUtilTests.cpp +++ /dev/null @@ -1,933 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class VerUtil - { - public: - [Fact] - void VerCompareVersionsTreatsMissingRevisionAsZero() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"1.2.3.4"; - LPCWSTR wzVersion2 = L"1.2.3"; - LPCWSTR wzVersion3 = L"1.2.3.0"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(4, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(2, pVersion2->dwMinor); - Assert::Equal(3, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(5, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(1, pVersion3->dwMajor); - Assert::Equal(2, pVersion3->dwMinor); - Assert::Equal(3, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(7, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - LPCWSTR wzVersion1 = L"1.0-2.0"; - LPCWSTR wzVersion2 = L"1.0-19"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(2, pVersion1->cReleaseLabels); - - Assert::Equal(TRUE, pVersion1->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion1->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); - Assert::Equal(4, pVersion1->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); - Assert::Equal(0, pVersion1->rgReleaseLabels[1].dwValue); - Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); - Assert::Equal(6, pVersion1->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(1, pVersion2->cReleaseLabels); - - Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); - Assert::Equal(19, pVersion2->rgReleaseLabels[0].dwValue); - Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabel); - Assert::Equal(4, pVersion2->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(6, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - } - } - - [Fact] - void VerCompareVersionsHandlesNormallyInvalidVersions() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - VERUTIL_VERSION* pVersion4 = NULL; - VERUTIL_VERSION* pVersion5 = NULL; - VERUTIL_VERSION* pVersion6 = NULL; - LPCWSTR wzVersion1 = L"10.-4.0"; - LPCWSTR wzVersion2 = L"10.-2.0"; - LPCWSTR wzVersion3 = L"0"; - LPCWSTR wzVersion4 = L""; - LPCWSTR wzVersion5 = L"10-2"; - LPCWSTR wzVersion6 = L"10-4.@"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); - - hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); - - hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(10, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(3, pVersion1->cchMetadataOffset); - Assert::Equal(TRUE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(10, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(3, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(0, pVersion3->dwMajor); - Assert::Equal(0, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(1, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); - Assert::Equal(0, pVersion4->dwMajor); - Assert::Equal(0, pVersion4->dwMinor); - Assert::Equal(0, pVersion4->dwPatch); - Assert::Equal(0, pVersion4->dwRevision); - Assert::Equal(0, pVersion4->cReleaseLabels); - Assert::Equal(0, pVersion4->cchMetadataOffset); - Assert::Equal(TRUE, pVersion4->fInvalid); - - NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); - Assert::Equal(10, pVersion5->dwMajor); - Assert::Equal(0, pVersion5->dwMinor); - Assert::Equal(0, pVersion5->dwPatch); - Assert::Equal(0, pVersion5->dwRevision); - Assert::Equal(1, pVersion5->cReleaseLabels); - - Assert::Equal(TRUE, pVersion5->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion5->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion5->rgReleaseLabels[0].cchLabel); - Assert::Equal(3, pVersion5->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(4, pVersion5->cchMetadataOffset); - Assert::Equal(FALSE, pVersion5->fInvalid); - - NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); - Assert::Equal(10, pVersion6->dwMajor); - Assert::Equal(0, pVersion6->dwMinor); - Assert::Equal(0, pVersion6->dwPatch); - Assert::Equal(0, pVersion6->dwRevision); - Assert::Equal(1, pVersion6->cReleaseLabels); - - Assert::Equal(TRUE, pVersion6->rgReleaseLabels[0].fNumeric); - Assert::Equal(4, pVersion6->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion6->rgReleaseLabels[0].cchLabel); - Assert::Equal(3, pVersion6->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(5, pVersion6->cchMetadataOffset); - Assert::Equal(TRUE, pVersion6->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1); - TestVerutilCompareParsedVersions(pVersion5, pVersion6, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - ReleaseVerutilVersion(pVersion4); - ReleaseVerutilVersion(pVersion5); - ReleaseVerutilVersion(pVersion6); - } - } - - [Fact] - void VerCompareVersionsTreatsHyphenAsVersionSeparator() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"0.0.1-a"; - LPCWSTR wzVersion2 = L"0-2"; - LPCWSTR wzVersion3 = L"1-2"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(0, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(1, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(1, pVersion1->cReleaseLabels); - - Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); - Assert::Equal(6, pVersion1->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(0, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(1, pVersion2->cReleaseLabels); - - Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion2->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); - Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(3, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(1, pVersion3->dwMajor); - Assert::Equal(0, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(1, pVersion3->cReleaseLabels); - - Assert::Equal(TRUE, pVersion3->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion3->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); - Assert::Equal(2, pVersion3->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(3, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsIgnoresLeadingZeroes() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - VERUTIL_VERSION* pVersion4 = NULL; - LPCWSTR wzVersion1 = L"0.01-a.1"; - LPCWSTR wzVersion2 = L"0.1.0-a.1"; - LPCWSTR wzVersion3 = L"0.1-a.b.0"; - LPCWSTR wzVersion4 = L"0.1.0-a.b.000"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(0, pVersion1->dwMajor); - Assert::Equal(1, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(2, pVersion1->cReleaseLabels); - - Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); - Assert::Equal(5, pVersion1->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion1->rgReleaseLabels[1].dwValue); - Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); - Assert::Equal(7, pVersion1->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(8, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(0, pVersion2->dwMajor); - Assert::Equal(1, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(2, pVersion2->cReleaseLabels); - - Assert::Equal(FALSE, pVersion2->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); - Assert::Equal(6, pVersion2->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(TRUE, pVersion2->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion2->rgReleaseLabels[1].dwValue); - Assert::Equal(1, pVersion2->rgReleaseLabels[1].cchLabel); - Assert::Equal(8, pVersion2->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(9, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(0, pVersion3->dwMajor); - Assert::Equal(1, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(3, pVersion3->cReleaseLabels); - - Assert::Equal(FALSE, pVersion3->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); - Assert::Equal(4, pVersion3->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(FALSE, pVersion3->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion3->rgReleaseLabels[1].cchLabel); - Assert::Equal(6, pVersion3->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(TRUE, pVersion3->rgReleaseLabels[2].fNumeric); - Assert::Equal(0, pVersion3->rgReleaseLabels[2].dwValue); - Assert::Equal(1, pVersion3->rgReleaseLabels[2].cchLabel); - Assert::Equal(8, pVersion3->rgReleaseLabels[2].cchLabelOffset); - - Assert::Equal(9, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); - Assert::Equal(0, pVersion4->dwMajor); - Assert::Equal(1, pVersion4->dwMinor); - Assert::Equal(0, pVersion4->dwPatch); - Assert::Equal(0, pVersion4->dwRevision); - Assert::Equal(3, pVersion4->cReleaseLabels); - - Assert::Equal(FALSE, pVersion4->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion4->rgReleaseLabels[0].cchLabel); - Assert::Equal(6, pVersion4->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(FALSE, pVersion4->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion4->rgReleaseLabels[1].cchLabel); - Assert::Equal(8, pVersion4->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(TRUE, pVersion4->rgReleaseLabels[2].fNumeric); - Assert::Equal(0, pVersion4->rgReleaseLabels[2].dwValue); - Assert::Equal(3, pVersion4->rgReleaseLabels[2].cchLabel); - Assert::Equal(10, pVersion4->rgReleaseLabels[2].cchLabelOffset); - - Assert::Equal(13, pVersion4->cchMetadataOffset); - Assert::Equal(FALSE, pVersion4->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); - TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - ReleaseVerutilVersion(pVersion4); - } - } - - [Fact] - void VerCompareVersionsTreatsUnexpectedContentAsMetadata() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"1.2.3+abcd"; - LPCWSTR wzVersion2 = L"1.2.3.abcd"; - LPCWSTR wzVersion3 = L"1.2.3.-abcd"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(6, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(2, pVersion2->dwMinor); - Assert::Equal(3, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(6, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(1, pVersion3->dwMajor); - Assert::Equal(2, pVersion3->dwMinor); - Assert::Equal(3, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(6, pVersion3->cchMetadataOffset); - Assert::Equal(TRUE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1); - TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsIgnoresLeadingV() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"10.20.30.40"; - LPCWSTR wzVersion2 = L"v10.20.30.40"; - LPCWSTR wzVersion3 = L"V10.20.30.40"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(10, pVersion1->dwMajor); - Assert::Equal(20, pVersion1->dwMinor); - Assert::Equal(30, pVersion1->dwPatch); - Assert::Equal(40, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(11, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion); - Assert::Equal(10, pVersion2->dwMajor); - Assert::Equal(20, pVersion2->dwMinor); - Assert::Equal(30, pVersion2->dwPatch); - Assert::Equal(40, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(11, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion); - Assert::Equal(10, pVersion3->dwMajor); - Assert::Equal(20, pVersion3->dwMinor); - Assert::Equal(30, pVersion3->dwPatch); - Assert::Equal(40, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(11, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); - TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsHandlesTooLargeNumbers() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295"; - LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(4294967295, pVersion1->dwMajor); - Assert::Equal(4294967295, pVersion1->dwMinor); - Assert::Equal(4294967295, pVersion1->dwPatch); - Assert::Equal(4294967295, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(43, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(0, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(0, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - } - } - - [Fact] - void VerCompareVersionsIgnoresMetadataForValidVersions() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - LPCWSTR wzVersion1 = L"1.2.3+abc"; - LPCWSTR wzVersion2 = L"1.2.3+xyz"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(6, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(2, pVersion2->dwMinor); - Assert::Equal(3, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(6, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - } - } - - [Fact] - void VerCopyVersionCopiesVersion() - { - HRESULT hr = S_OK; - LPCWSTR wzVersion = L"1.2.3.4+abc123"; - VERUTIL_VERSION* pSource = NULL; - VERUTIL_VERSION* pCopy = NULL; - int nResult = 0; - - try - { - hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); - NativeAssert::Succeeded(hr, "VerParseVersion failed"); - - NativeAssert::StringEqual(wzVersion, pSource->sczVersion); - Assert::Equal(1, pSource->dwMajor); - Assert::Equal(2, pSource->dwMinor); - Assert::Equal(3, pSource->dwPatch); - Assert::Equal(4, pSource->dwRevision); - Assert::Equal(0, pSource->cReleaseLabels); - - Assert::Equal(8, pSource->cchMetadataOffset); - Assert::Equal(FALSE, pSource->fInvalid); - - hr = VerCopyVersion(pSource, &pCopy); - NativeAssert::Succeeded(hr, "VerCopyVersion failed"); - - Assert::False(pSource == pCopy); - Assert::False(pSource->sczVersion == pCopy->sczVersion); - - hr = VerCompareParsedVersions(pSource, pCopy, &nResult); - NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); - - Assert::Equal(nResult, 0); - } - finally - { - ReleaseVerutilVersion(pCopy); - ReleaseVerutilVersion(pSource); - } - } - - [Fact] - void VerCopyVersionCopiesPrereleaseVersion() - { - HRESULT hr = S_OK; - LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123"; - VERUTIL_VERSION* pSource = NULL; - VERUTIL_VERSION* pCopy = NULL; - int nResult = 0; - - try - { - hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); - NativeAssert::Succeeded(hr, "VerParseVersion failed"); - - NativeAssert::StringEqual(wzVersion, pSource->sczVersion); - Assert::Equal(1, pSource->dwMajor); - Assert::Equal(2, pSource->dwMinor); - Assert::Equal(3, pSource->dwPatch); - Assert::Equal(4, pSource->dwRevision); - Assert::Equal(5, pSource->cReleaseLabels); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[0].cchLabel); - Assert::Equal(8, pSource->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[1].cchLabel); - Assert::Equal(10, pSource->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[2].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[2].cchLabel); - Assert::Equal(12, pSource->rgReleaseLabels[2].cchLabelOffset); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[3].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[3].cchLabel); - Assert::Equal(14, pSource->rgReleaseLabels[3].cchLabelOffset); - - Assert::Equal(TRUE, pSource->rgReleaseLabels[4].fNumeric); - Assert::Equal(5, pSource->rgReleaseLabels[4].dwValue); - Assert::Equal(1, pSource->rgReleaseLabels[4].cchLabel); - Assert::Equal(16, pSource->rgReleaseLabels[4].cchLabelOffset); - - Assert::Equal(18, pSource->cchMetadataOffset); - Assert::Equal(TRUE, pSource->fInvalid); - - hr = VerCopyVersion(pSource, &pCopy); - NativeAssert::Succeeded(hr, "VerCopyVersion failed"); - - Assert::False(pSource == pCopy); - Assert::False(pSource->sczVersion == pCopy->sczVersion); - Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels); - - hr = VerCompareParsedVersions(pSource, pCopy, &nResult); - NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); - - Assert::Equal(nResult, 0); - } - finally - { - ReleaseVerutilVersion(pCopy); - ReleaseVerutilVersion(pSource); - } - } - - [Fact] - void VerParseVersionTreatsTrailingDotsAsInvalid() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - VERUTIL_VERSION* pVersion4 = NULL; - VERUTIL_VERSION* pVersion5 = NULL; - VERUTIL_VERSION* pVersion6 = NULL; - VERUTIL_VERSION* pVersion7 = NULL; - LPCWSTR wzVersion1 = L"."; - LPCWSTR wzVersion2 = L"1."; - LPCWSTR wzVersion3 = L"2.1."; - LPCWSTR wzVersion4 = L"3.2.1."; - LPCWSTR wzVersion5 = L"4.3.2.1."; - LPCWSTR wzVersion6 = L"5-."; - LPCWSTR wzVersion7 = L"6-a."; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); - - hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); - - hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); - - hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(0, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(0, pVersion1->cchMetadataOffset); - Assert::Equal(TRUE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(2, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(2, pVersion3->dwMajor); - Assert::Equal(1, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(4, pVersion3->cchMetadataOffset); - Assert::Equal(TRUE, pVersion3->fInvalid); - - NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); - Assert::Equal(3, pVersion4->dwMajor); - Assert::Equal(2, pVersion4->dwMinor); - Assert::Equal(1, pVersion4->dwPatch); - Assert::Equal(0, pVersion4->dwRevision); - Assert::Equal(0, pVersion4->cReleaseLabels); - Assert::Equal(6, pVersion4->cchMetadataOffset); - Assert::Equal(TRUE, pVersion4->fInvalid); - - NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); - Assert::Equal(4, pVersion5->dwMajor); - Assert::Equal(3, pVersion5->dwMinor); - Assert::Equal(2, pVersion5->dwPatch); - Assert::Equal(1, pVersion5->dwRevision); - Assert::Equal(0, pVersion5->cReleaseLabels); - Assert::Equal(8, pVersion5->cchMetadataOffset); - Assert::Equal(TRUE, pVersion5->fInvalid); - - NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); - Assert::Equal(5, pVersion6->dwMajor); - Assert::Equal(0, pVersion6->dwMinor); - Assert::Equal(0, pVersion6->dwPatch); - Assert::Equal(0, pVersion6->dwRevision); - Assert::Equal(0, pVersion6->cReleaseLabels); - Assert::Equal(2, pVersion6->cchMetadataOffset); - Assert::Equal(TRUE, pVersion6->fInvalid); - - NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion); - Assert::Equal(6, pVersion7->dwMajor); - Assert::Equal(0, pVersion7->dwMinor); - Assert::Equal(0, pVersion7->dwPatch); - Assert::Equal(0, pVersion7->dwRevision); - Assert::Equal(1, pVersion7->cReleaseLabels); - - Assert::Equal(FALSE, pVersion7->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion7->rgReleaseLabels[0].cchLabel); - Assert::Equal(2, pVersion7->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(4, pVersion7->cchMetadataOffset); - Assert::Equal(TRUE, pVersion7->fInvalid); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - ReleaseVerutilVersion(pVersion4); - ReleaseVerutilVersion(pVersion5); - ReleaseVerutilVersion(pVersion6); - ReleaseVerutilVersion(pVersion7); - } - } - - [Fact] - void VerVersionFromQwordCreatesVersion() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - - try - { - hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1); - NativeAssert::Succeeded(hr, "VerVersionFromQword failed"); - - NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(4, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - } - finally - { - ReleaseVerutilVersion(pVersion1); - } - } - - private: - void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult) - { - HRESULT hr = S_OK; - int nResult = 0; - - hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); - NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); - - Assert::Equal(nExpectedResult, nResult); - - hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult); - NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); - - Assert::Equal(nExpectedResult, -nResult); - } - }; -} diff --git a/src/test/DUtilUnitTest/error.cpp b/src/test/DUtilUnitTest/error.cpp deleted file mode 100644 index e51971c3..00000000 --- a/src/test/DUtilUnitTest/error.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -const int ERROR_STRING_BUFFER = 1024; - -static char szMsg[ERROR_STRING_BUFFER]; -static WCHAR wzMsg[ERROR_STRING_BUFFER]; - -void CALLBACK DutilTestTraceError( - __in_z LPCSTR /*szFile*/, - __in int /*iLine*/, - __in REPORT_LEVEL /*rl*/, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - if (DUTIL_SOURCE_EXTERNAL == source) - { - ::StringCchPrintfA(szMsg, countof(szMsg), szFormat, args); - MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); - throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrError, gcnew System::String(wzMsg))); - } -} diff --git a/src/test/DUtilUnitTest/error.h b/src/test/DUtilUnitTest/error.h deleted file mode 100644 index b973acaf..00000000 --- a/src/test/DUtilUnitTest/error.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL - -void CALLBACK DutilTestTraceError( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config deleted file mode 100644 index a4fef2bf..00000000 --- a/src/test/DUtilUnitTest/packages.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/DUtilUnitTest/precomp.cpp b/src/test/DUtilUnitTest/precomp.cpp deleted file mode 100644 index 37664a1c..00000000 --- a/src/test/DUtilUnitTest/precomp.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h deleted file mode 100644 index e9f8770b..00000000 --- a/src/test/DUtilUnitTest/precomp.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include - -// Include error.h before dutil.h -#include -#include "error.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // NOTE: this must come after atomutil.h and rssutil.h since it uses them. -#include -#include - -#pragma managed -#include diff --git a/src/test/DUtilUnitTest/resource.h b/src/test/DUtilUnitTest/resource.h deleted file mode 100644 index bdf252f6..00000000 --- a/src/test/DUtilUnitTest/resource.h +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - diff --git a/src/version.json b/src/version.json new file mode 100644 index 00000000..5f857771 --- /dev/null +++ b/src/version.json @@ -0,0 +1,11 @@ +{ + "version": "4.0", + "publicReleaseRefSpec": [ + "^refs/heads/master$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} diff --git a/version.json b/version.json deleted file mode 100644 index 5f857771..00000000 --- a/version.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "4.0", - "publicReleaseRefSpec": [ - "^refs/heads/master$" - ], - "cloudBuild": { - "buildNumber": { - "enabled": true - } - } -} -- cgit v1.2.3-55-g6feb